@go-to-k/cdkd 0.290.31 → 0.290.32
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-DNzgk-BS.js → asg-provider-BuEvX2d9.js} +3 -3
- package/dist/{asg-provider-DNzgk-BS.js.map → asg-provider-BuEvX2d9.js.map} +1 -1
- package/dist/cli.js +2 -2
- package/dist/{deploy-engine-21iNqwR3.js → deploy-engine-CfOsVRQB.js} +370 -91
- package/dist/deploy-engine-CfOsVRQB.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/{logger-BjWM-UVj.js → logger-BX4pJk0T.js} +53 -2
- package/dist/logger-BX4pJk0T.js.map +1 -0
- package/dist/{program-CnSaRdY9.js → program-DdmCUgOP.js} +155 -86
- package/dist/{program-CnSaRdY9.js.map → program-DdmCUgOP.js.map} +1 -1
- package/dist/{version-IwTfNQ-K.js → version-F41ektvu.js} +2 -2
- package/dist/{version-IwTfNQ-K.js.map → version-F41ektvu.js.map} +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-21iNqwR3.js.map +0 -1
- package/dist/logger-BjWM-UVj.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { C as displayAwsMessage,
|
|
1
|
+
import { C as displayAwsMessage, D as truncateCodePoints, E as isPasteableIdent, 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-BX4pJk0T.js";
|
|
2
2
|
import { a as setAssumedRoleCredentials, i as getPreAssumeEnvCredentials, n as awsClientDefaults, o as setPreAssumeEnvCredentials, r as getAssumedRoleCredentials, t as SECOND_ROLE_PUBLISH_REFUSAL } from "./aws-client-defaults-BoIxyCir.js";
|
|
3
|
-
import { t as getCdkdVersion } from "./version-
|
|
3
|
+
import { t as getCdkdVersion } from "./version-F41ektvu.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";
|
|
@@ -9980,18 +9980,54 @@ function buildForceUnlockCommand(stackName, region, recovery) {
|
|
|
9980
9980
|
const stackIsExact = safeStack === stackName;
|
|
9981
9981
|
const safeRegion = region === void 0 ? void 0 : displaySafe(region, { asciiOnly: true });
|
|
9982
9982
|
const regionIsExact = region === void 0 || safeRegion === region;
|
|
9983
|
-
const
|
|
9984
|
-
|
|
9985
|
-
|
|
9986
|
-
|
|
9983
|
+
const recoveryFlags = recoveryCommandFlags(recovery);
|
|
9984
|
+
if (!safeStack || safeRegion === "" || !stackIsExact || !regionIsExact || !recoveryFlags.exact) return "";
|
|
9985
|
+
return [safeRegion === void 0 ? `cdkd force-unlock ${shellQuote(safeStack)}` : `cdkd force-unlock ${shellQuote(safeStack)} --stack-region ${shellQuote(safeRegion)}`, ...recoveryFlags.flags].join(" ");
|
|
9986
|
+
}
|
|
9987
|
+
/**
|
|
9988
|
+
* A placeholder for a value a pasteable command could not name, QUOTED.
|
|
9989
|
+
*
|
|
9990
|
+
* A bare `<profile>` is two shell redirections, not a word: pasted, `<profile`
|
|
9991
|
+
* reads stdin from a file named `profile` and `>` sends stdout to whatever
|
|
9992
|
+
* word follows. While the hole was the command's LAST word that was only a
|
|
9993
|
+
* syntax error; with flags appended after it (`--profile <profile>
|
|
9994
|
+
* --state-bucket b`) it ran the command with `--state-bucket` swallowed as a
|
|
9995
|
+
* redirect target and `b` as the profile — a delete against the ambient
|
|
9996
|
+
* bucket, with nothing printed (measured by the maintainer, M4 of the
|
|
9997
|
+
* go-to-k/cdkd#3363 review). Quoted, it pastes as one literal argument and
|
|
9998
|
+
* nothing else — no redirection, no swallowed flag. It is not refused: an
|
|
9999
|
+
* UNFILLED hole runs as that literal value (a stack named `<stack>`, a prefix
|
|
10000
|
+
* `<prefix>`).
|
|
10001
|
+
*/
|
|
10002
|
+
function commandHole(name) {
|
|
10003
|
+
return `'<${name}>'`;
|
|
10004
|
+
}
|
|
10005
|
+
/**
|
|
10006
|
+
* The flags that pin a pasteable state command to the caller's account and
|
|
10007
|
+
* bucket: `--profile`, the resolved `--state-bucket`, and a NON-default
|
|
10008
|
+
* `--state-prefix` (see {@link LockRecoveryContext} for why each is emitted
|
|
10009
|
+
* when it is). ONE spelling, shared by {@link buildForceUnlockCommand} and the
|
|
10010
|
+
* `cdkd orphan` properties refusal `malformed-resources-bag.ts` builds
|
|
10011
|
+
* (go-to-k/cdkd#3363): after `cdkd orphan --profile prod ...` a remedy that
|
|
10012
|
+
* carried none of these resolved the DEFAULT profile's bucket when pasted.
|
|
10013
|
+
* Each fragment takes {@link sanitizeRecoveryValue}'s sanitize + exactness
|
|
10014
|
+
* pair (go-to-k/cdkd#3377); what a caller does with an inexact one is its
|
|
10015
|
+
* own call — `buildForceUnlockCommand` suppresses the whole command, the orphan
|
|
10016
|
+
* refusal prints the hole.
|
|
10017
|
+
*/
|
|
10018
|
+
function recoveryCommandFlags(recovery) {
|
|
10019
|
+
const profile = sanitizeRecoveryValue(recovery?.profile);
|
|
10020
|
+
const stateBucket = sanitizeRecoveryValue(recovery?.stateBucket);
|
|
10021
|
+
const statePrefix = sanitizeRecoveryValue(recovery?.statePrefix);
|
|
10022
|
+
const flags = [];
|
|
10023
|
+
if (profile.text || !profile.exact) flags.push(profile.exact ? `--profile ${shellQuote(profile.text)}` : `--profile ${commandHole("profile")}`);
|
|
10024
|
+
if (stateBucket.text || !stateBucket.exact) flags.push(stateBucket.exact ? `--state-bucket ${shellQuote(stateBucket.text)}` : `--state-bucket ${commandHole("bucket")}`);
|
|
10025
|
+
const prefix = recovery?.statePrefix;
|
|
10026
|
+
if (prefix !== void 0 && prefix !== "cdkd") flags.push(statePrefix.exact ? `--state-prefix ${shellQuote(statePrefix.text)}` : `--state-prefix ${commandHole("prefix")}`);
|
|
10027
|
+
return {
|
|
10028
|
+
flags,
|
|
10029
|
+
exact: profile.exact && stateBucket.exact && statePrefix.exact
|
|
9987
10030
|
};
|
|
9988
|
-
const recoveryIsExact = Object.values(safeRecovery).every((v) => v.exact);
|
|
9989
|
-
if (!safeStack || safeRegion === "" || !stackIsExact || !regionIsExact || !recoveryIsExact) return "";
|
|
9990
|
-
const parts = [safeRegion === void 0 ? `cdkd force-unlock ${shellQuote(safeStack)}` : `cdkd force-unlock ${shellQuote(safeStack)} --stack-region ${shellQuote(safeRegion)}`];
|
|
9991
|
-
if (safeRecovery.profile.text) parts.push(`--profile ${shellQuote(safeRecovery.profile.text)}`);
|
|
9992
|
-
if (safeRecovery.stateBucket.text) parts.push(`--state-bucket ${shellQuote(safeRecovery.stateBucket.text)}`);
|
|
9993
|
-
if (safeRecovery.statePrefix.text && safeRecovery.statePrefix.text !== "cdkd") parts.push(`--state-prefix ${shellQuote(safeRecovery.statePrefix.text)}`);
|
|
9994
|
-
return parts.join(" ");
|
|
9995
10031
|
}
|
|
9996
10032
|
/**
|
|
9997
10033
|
* Sanitize ONE {@link LockRecoveryContext} fragment and report whether that
|
|
@@ -21847,19 +21883,70 @@ function stackClause(stackName, region) {
|
|
|
21847
21883
|
* `destroy-runner.ts`; since go-to-k/cdkd#3328 the first operand is the S3
|
|
21848
21884
|
* KEY's region rather than the record body's, which is a key SEGMENT and so
|
|
21849
21885
|
* still plantable) -- its stack name is the caller's, so the region alone is
|
|
21850
|
-
* what would aim a destructive command using a value cdkd does not own. This
|
|
21851
|
-
*
|
|
21852
|
-
* omitting the region is the wider action. Two
|
|
21853
|
-
* module; neither is the general rule.
|
|
21886
|
+
* what would aim a destructive command using a value cdkd does not own. This
|
|
21887
|
+
* one is reached only with the caller's synthesized stack and
|
|
21888
|
+
* `pickStackRegion`'s answer, so omitting the region is the wider action. Two
|
|
21889
|
+
* opposite precedents in one module; neither is the general rule.
|
|
21890
|
+
*
|
|
21891
|
+
* **Substitution is GATED on the identity rendering EXACTLY, at
|
|
21892
|
+
* `STACK_REF_MAX_CODE_POINTS`** (go-to-k/cdkd#3360, and the M0 finding of the
|
|
21893
|
+
* go-to-k/cdkd#3363 review). The synthesized name is not validated: a prebuilt
|
|
21894
|
+
* cloud assembly's manifest reaches `cdkd orphan` unread, so a name `safeIdentifier`
|
|
21895
|
+
* would TRIM (`'prod-api '`) substitutes as a healthy sibling's, and one it would
|
|
21896
|
+
* TRUNCATE (a 166-code-point nested name at the 128 default) becomes a command
|
|
21897
|
+
* `cdkd state orphan` resolves to zero records and reports as a skip at exit 0.
|
|
21898
|
+
* Since go-to-k/cdkd#3359 the legacy shape carries no `--stack-region`, so the
|
|
21899
|
+
* name is the ONLY thing narrowing this DELETE. The template arm is the fallback,
|
|
21900
|
+
* and the caller says where to take the name from instead
|
|
21901
|
+
* ({@link rendersExactly} is the test, shared with the inspect line and the
|
|
21902
|
+
* object path).
|
|
21903
|
+
*
|
|
21904
|
+
* `recovery` qualifies EVERY arm, the no-identity template included, the way
|
|
21905
|
+
* {@link buildForceUnlockCommand} is qualified, through the same `recoveryCommandFlags`: `cdkd state orphan`
|
|
21906
|
+
* re-resolves the bucket from the ambient profile, so a remedy pasted after
|
|
21907
|
+
* `cdkd orphan --profile prod ...` would otherwise address the default
|
|
21908
|
+
* profile's account. The template keeps them too: the identity is the hole,
|
|
21909
|
+
* not the account, and an operator who fills only the hole would otherwise run
|
|
21910
|
+
* an UNQUALIFIED delete — which `cdkd state orphan` reports as a skip at exit 0
|
|
21911
|
+
* when the default bucket has no such record, and offers to delete when it has
|
|
21912
|
+
* one (the M3 finding of the go-to-k/cdkd#3363 review).
|
|
21913
|
+
*/
|
|
21914
|
+
function dropRecordCommand(stackName, region, recovery) {
|
|
21915
|
+
if (stackName === void 0 || stackName === "") return [`cdkd state orphan ${commandHole("stack")} --stack-region ${commandHole("region")}`, ...recoveryCommandFlags(recovery).flags].join(" ");
|
|
21916
|
+
const known = region === void 0 || region === "" ? void 0 : region;
|
|
21917
|
+
const regionExact = known === void 0 || rendersExactly(known);
|
|
21918
|
+
if (!rendersExactly(stackName) || !regionExact) {
|
|
21919
|
+
const flag = known === void 0 ? "" : regionExact ? ` --stack-region ${shellQuote(known)}` : ` --stack-region ${commandHole("region")}`;
|
|
21920
|
+
return [`cdkd state orphan ${commandHole("stack")}${flag}`, ...recoveryCommandFlags(recovery).flags].join(" ");
|
|
21921
|
+
}
|
|
21922
|
+
const flag = known === void 0 ? "" : ` --stack-region ${shellQuote(known)}`;
|
|
21923
|
+
return [`cdkd state orphan ${shellQuote(stackName)}${flag}`, ...recoveryCommandFlags(recovery).flags].join(" ");
|
|
21924
|
+
}
|
|
21925
|
+
/**
|
|
21926
|
+
* Whether an identity survives {@link safeIdentifier} UNCHANGED at the
|
|
21927
|
+
* state-record grammar's cap, so a command or a key built from it addresses the
|
|
21928
|
+
* record the message is about. The cap is `STACK_REF_MAX_CODE_POINTS`, not the
|
|
21929
|
+
* 128 the prose uses: `cdkd orphan` reads a PREBUILT assembly's stack name
|
|
21930
|
+
* unvalidated, so a manifest can carry one past 128 that is nonetheless the
|
|
21931
|
+
* record's real key, and truncating it names nothing — the same bound
|
|
21932
|
+
* `malformedDestroyResourcesRefusalMessage` uses, reached from a different
|
|
21933
|
+
* source.
|
|
21934
|
+
*
|
|
21935
|
+
* What it covers: every identity the `cdkd orphan` properties refusal builds
|
|
21936
|
+
* something PASTEABLE from — the drop command ({@link dropRecordCommand}), the
|
|
21937
|
+
* `cdkd state show` line ({@link orphanInspectCommand}) and the object path
|
|
21938
|
+
* ({@link orphanInspectClause}) — so no two of them can disagree about a name
|
|
21939
|
+
* (go-to-k/cdkd#3363 review, M0 and M2). What it does NOT cover yet: the
|
|
21940
|
+
* destroy refusal and the divergent-region refusal above still spell the same
|
|
21941
|
+
* test inline, and the shared {@link inspectCommand} is ungated at 128 for its
|
|
21942
|
+
* other callers.
|
|
21854
21943
|
*/
|
|
21855
|
-
function
|
|
21856
|
-
|
|
21857
|
-
const flag = region === void 0 || region === "" ? "" : ` --stack-region ${shellQuote(safeIdentifier(region))}`;
|
|
21858
|
-
return `cdkd state orphan ${shellQuote(safeIdentifier(stackName))}${flag}`;
|
|
21944
|
+
function rendersExactly(value) {
|
|
21945
|
+
return safeIdentifier(value, STACK_REF_MAX_CODE_POINTS) === value;
|
|
21859
21946
|
}
|
|
21860
21947
|
/** The remedy command {@link stackClause}'s message ends on. */
|
|
21861
21948
|
function inspectCommand(stackName, region) {
|
|
21862
|
-
if (stackName === void 0) return
|
|
21949
|
+
if (stackName === void 0) return `cdkd state show ${commandHole("stack")} --stack-region ${commandHole("region")} --json`;
|
|
21863
21950
|
const flag = region === void 0 ? "" : ` --stack-region ${shellQuote(safeIdentifier(region))}`;
|
|
21864
21951
|
return `cdkd state show ${shellQuote(safeIdentifier(stackName))}${flag} --json`;
|
|
21865
21952
|
}
|
|
@@ -22164,10 +22251,144 @@ function malformedResourcePropertiesRefusalMessage(stackName, region, logicalIds
|
|
|
22164
22251
|
* stack name and the region `pickStackRegion` settled on, never the record's
|
|
22165
22252
|
* own unvalidated self-report. See {@link stackClause}.
|
|
22166
22253
|
*/
|
|
22167
|
-
function malformedOrphanResourcePropertiesRefusalMessage(rawStackName, rawRegion, logicalIds) {
|
|
22254
|
+
function malformedOrphanResourcePropertiesRefusalMessage(rawStackName, rawRegion, logicalIds, recovery) {
|
|
22168
22255
|
const stackName = rawStackName === "" ? void 0 : rawStackName;
|
|
22169
22256
|
const region = rawRegion === "" ? void 0 : rawRegion;
|
|
22170
|
-
|
|
22257
|
+
const inspect = orphanInspectClause(stackName, region, recovery);
|
|
22258
|
+
const listCommand = withheldIdentityListCommand(stackName, region, recovery);
|
|
22259
|
+
const commands = [
|
|
22260
|
+
`Drop the record: ${dropRecordCommand(stackName, region, recovery)}`,
|
|
22261
|
+
...listCommand === void 0 ? [] : [`Find the exact name: ${listCommand}`],
|
|
22262
|
+
...inspect.command === void 0 ? [] : [`Inspect the record: ${inspect.command}`]
|
|
22263
|
+
];
|
|
22264
|
+
return [
|
|
22265
|
+
`${namedPropertyBagsClause(stackName, region, logicalIds)} 'cdkd orphan' REWRITES and SAVES every record it keeps, so it refuses rather than continuing — under '--dry-run' too, because the rewrite audit table a dry run prints is the wrong one. It is not that the save would fabricate a map: an unreadable bag is carried through VERBATIM. It is that this command exists to leave the record deployable by rewriting every reference to an orphaned resource, and a map it cannot read hides whichever references it holds — a string or a number presents none to find, and a list is walked, so its rewrites are recorded into a container that is still not a map. Continuing would rewrite, save, and report success over a record 'cdkd deploy' then REFUSES. No state was written. Two ways out need no CDK app: repair the record by hand, or drop it whole with the 'Drop the record' command below, which leaves the live AWS resources standing${withheldIdentityClause(stackName, region)}${withheldRecoveryClause(recovery)}. A third works only while the CDK app STILL DECLARES the named resource — this refusal covers just the records that would SURVIVE the save, so 'cdkd orphan <its construct path>' removes it and repairs the rest; construct paths come from the synthesized template, so a resource the app no longer declares has none and must take one of the first two.` + (inspect.sentence === void 0 ? "" : ` ${inspect.sentence}`),
|
|
22266
|
+
...commands,
|
|
22267
|
+
...inspect.locations ?? []
|
|
22268
|
+
].join("\n");
|
|
22269
|
+
}
|
|
22270
|
+
/**
|
|
22271
|
+
* The half-sentence that accompanies a TEMPLATE drop remedy when a stack name
|
|
22272
|
+
* WAS known but it, or the region beside it, did not render exactly. The prose
|
|
22273
|
+
* above it prints the sanitized spelling, and an operator filling the template
|
|
22274
|
+
* from that spelling would be aiming at the healthy sibling the gate exists to
|
|
22275
|
+
* protect (`'prod-api '` renders as `prod-api`; a padded region is the same
|
|
22276
|
+
* misdirection one flag over), so the message says where to take them from
|
|
22277
|
+
* instead: `cdkd state list --json`, which writes the raw name
|
|
22278
|
+
* through `JSON.stringify` — `--long` renders through `displayIdent`, which
|
|
22279
|
+
* trims, so it would hand back the same spelling. Empty when the command was
|
|
22280
|
+
* substituted, and when no name was known at all — that arm's template already
|
|
22281
|
+
* reads as a hole to fill.
|
|
22282
|
+
*/
|
|
22283
|
+
function withheldIdentityClause(stackName, region) {
|
|
22284
|
+
if (!identityWithheld(stackName, region)) return "";
|
|
22285
|
+
return " — the stack name or region above did not render exactly, so take them from the 'Find the exact name' command below — replace each quoted hole, quotes included, with the shell-quoted value — rather than from this message";
|
|
22286
|
+
}
|
|
22287
|
+
/**
|
|
22288
|
+
* The half-sentence for the OTHER thing that can print as a hole: one of the
|
|
22289
|
+
* account fragments `recoveryCommandFlags` appends.
|
|
22290
|
+
*
|
|
22291
|
+
* Separate from {@link withheldIdentityClause} rather than folded into it,
|
|
22292
|
+
* because the two holes have different SOURCES and therefore different remedies.
|
|
22293
|
+
* An identity hole is filled from `cdkd state list --json`, which is why that
|
|
22294
|
+
* clause points at a command; an account fragment came from the operator's own
|
|
22295
|
+
* argv, so they already hold it and no listing would help. Conflating them would
|
|
22296
|
+
* send someone to a listing that does not carry their `--profile`.
|
|
22297
|
+
*
|
|
22298
|
+
* Without this, a run whose PREFIX or BUCKET was the inexact value printed
|
|
22299
|
+
* `--state-prefix '<prefix>'` with nothing in the message saying the hole was a
|
|
22300
|
+
* hole: `identityWithheld` reads the stack name and the region only, and the
|
|
22301
|
+
* object-path note is suppressed whenever a prefix WAS supplied. That is
|
|
22302
|
+
* {@link withheldIdentityClause}'s own defect class, one fragment over.
|
|
22303
|
+
*/
|
|
22304
|
+
function withheldRecoveryClause(recovery) {
|
|
22305
|
+
if (recoveryCommandFlags(recovery).exact) return "";
|
|
22306
|
+
return " — an account fragment ('--profile', '--state-bucket' or '--state-prefix') did not render exactly, so the command lines below print a quoted hole in its place, which you replace whole — quotes included — with the SHELL-QUOTED value you passed this run";
|
|
22307
|
+
}
|
|
22308
|
+
/**
|
|
22309
|
+
* The listing {@link withheldIdentityClause} points at, printed as its own
|
|
22310
|
+
* trailing command line. Qualified like the remedy it stands in for: a bare
|
|
22311
|
+
* listing after `cdkd orphan --profile prod ...` would read the DEFAULT
|
|
22312
|
+
* profile's bucket, and the name it hands back would then be a different
|
|
22313
|
+
* bucket's.
|
|
22314
|
+
*/
|
|
22315
|
+
function withheldIdentityListCommand(stackName, region, recovery) {
|
|
22316
|
+
if (!identityWithheld(stackName, region)) return void 0;
|
|
22317
|
+
return ["cdkd state list --json", ...recoveryCommandFlags(recovery).flags].join(" ");
|
|
22318
|
+
}
|
|
22319
|
+
/** A name WAS known, and it or the region beside it did not render exactly. */
|
|
22320
|
+
function identityWithheld(stackName, region) {
|
|
22321
|
+
if (stackName === void 0 || stackName === "") return false;
|
|
22322
|
+
const known = region === "" ? void 0 : region;
|
|
22323
|
+
return !(rendersExactly(stackName) && (known === void 0 || rendersExactly(known)));
|
|
22324
|
+
}
|
|
22325
|
+
/**
|
|
22326
|
+
* The `cdkd state show` command on the orphan properties refusal's trailing
|
|
22327
|
+
* `Inspect the record:` line, for a record listed WITH a region — gated and
|
|
22328
|
+
* qualified exactly as the drop command on the line above it is, because it is
|
|
22329
|
+
* the same identity in the same message.
|
|
22330
|
+
* Gating only the drop command left the message contradicting itself: it said
|
|
22331
|
+
* the name did not render exactly, pointed at `cdkd state list --json`, and then
|
|
22332
|
+
* printed `cdkd state show prod-api ...` with the trimmed spelling of
|
|
22333
|
+
* `'prod-api '`, naming the healthy sibling (the M2 finding of the
|
|
22334
|
+
* go-to-k/cdkd#3363 review). Holes follow {@link dropRecordCommand}'s rule: an
|
|
22335
|
+
* altered region makes both a hole, an altered name only the name.
|
|
22336
|
+
*
|
|
22337
|
+
* Built here rather than by widening {@link inspectCommand}, which serves four
|
|
22338
|
+
* other builders whose identities come from different sources (and this one no
|
|
22339
|
+
* longer calls it at all).
|
|
22340
|
+
*/
|
|
22341
|
+
function orphanInspectCommand(stackName, region, recovery) {
|
|
22342
|
+
const regionExact = rendersExactly(region);
|
|
22343
|
+
return [`cdkd state show ${regionExact && rendersExactly(stackName) ? shellQuote(stackName) : commandHole("stack")} --stack-region ${regionExact ? shellQuote(region) : commandHole("region")} --json`, ...recoveryCommandFlags(recovery).flags].join(" ");
|
|
22344
|
+
}
|
|
22345
|
+
/**
|
|
22346
|
+
* How {@link malformedOrphanResourcePropertiesRefusalMessage} tells the operator
|
|
22347
|
+
* to inspect the record: a `command` it prints on its trailing
|
|
22348
|
+
* `Inspect the record:` line, or — for the legacy region-less shape, where no
|
|
22349
|
+
* command can serve — a prose `sentence` naming the S3 object.
|
|
22350
|
+
*
|
|
22351
|
+
* A KNOWN stack with NO region is a legacy record (`<prefix>/<stack>/state.json`)
|
|
22352
|
+
* that `listStacks` lists with no region — `cdkd orphan` hands this module the
|
|
22353
|
+
* region the record is LISTED under since go-to-k/cdkd#3359, and that is
|
|
22354
|
+
* `undefined` only for such a ref (its body names no region, or the probe that
|
|
22355
|
+
* reads it could not). `cdkd state show` refuses such a record with or without
|
|
22356
|
+
* `--stack-region` ("has only a legacy state record without a region"), and the
|
|
22357
|
+
* migration it suggests is a `cdkd deploy`, which refuses this very record
|
|
22358
|
+
* (go-to-k/cdkd#3191). So ending on `cdkd state show` would hand the operator a
|
|
22359
|
+
* command that cannot serve them; the line names the S3 object instead. Teaching
|
|
22360
|
+
* `state show` to read a legacy record was the alternative, declined as a wider
|
|
22361
|
+
* change to a command that is not the one refusing.
|
|
22362
|
+
*
|
|
22363
|
+
* The object path is printed only when the stack name {@link rendersExactly}: a
|
|
22364
|
+
* path with a sanitized or truncated segment names an object that does not
|
|
22365
|
+
* exist. The name comes from the assembly, and `cdkd orphan` accepts a PREBUILT
|
|
22366
|
+
* one whose manifest nothing validates, which is what makes an inexact name
|
|
22367
|
+
* reachable at all (go-to-k/cdkd#3360); an S3 key's `Parent~Child` name, the
|
|
22368
|
+
* case the destroy refusal's gate was written for, cannot appear here.
|
|
22369
|
+
*
|
|
22370
|
+
* With `recovery` the path carries the REAL prefix and names the bucket, the
|
|
22371
|
+
* way the drop remedy above it is qualified; without it the prefix is a
|
|
22372
|
+
* placeholder and the sentence says how to fill it.
|
|
22373
|
+
*/
|
|
22374
|
+
function orphanInspectClause(stackName, region, recovery) {
|
|
22375
|
+
if (stackName === void 0 || stackName === "") return { command: [`cdkd state show ${commandHole("stack")} --stack-region ${commandHole("region")} --json`, ...recoveryCommandFlags(recovery).flags].join(" ") };
|
|
22376
|
+
if (region !== void 0) return { command: orphanInspectCommand(stackName, region, recovery) };
|
|
22377
|
+
const lead = "The record is listed with no region — a legacy 'state.json' whose body names none, or one the listing could not read — which 'cdkd state show' cannot read and a 'cdkd deploy' will not migrate while it is torn, so inspect the object directly";
|
|
22378
|
+
const exactOrUndefined = (v) => v !== void 0 && sanitizeRecoveryValue(v).exact ? v : void 0;
|
|
22379
|
+
const bucket = exactOrUndefined(recovery?.stateBucket) || void 0;
|
|
22380
|
+
const locations = bucket === void 0 ? [] : [`State bucket: ${shellQuote(bucket)}`];
|
|
22381
|
+
const where = bucket === void 0 ? `, in the state bucket ('cdkd state info' names it)` : `, in the bucket on the 'State bucket' line below`;
|
|
22382
|
+
if (!rendersExactly(stackName)) return {
|
|
22383
|
+
sentence: `${lead}: it is the legacy 'state.json' under this stack name, which did not render exactly${where}.`,
|
|
22384
|
+
locations
|
|
22385
|
+
};
|
|
22386
|
+
const prefix = exactOrUndefined(recovery?.statePrefix);
|
|
22387
|
+
const key = shellQuote(`${prefix ?? "<prefix>"}/${stackName}/state.json`);
|
|
22388
|
+
return {
|
|
22389
|
+
sentence: `${lead}: the 'Object key' line below names it${where}.${recovery?.statePrefix === void 0 ? ` Its prefix is 'cdkd' unless '--state-prefix' was given.` : prefix === void 0 ? " The prefix you passed did not render exactly, so the key shows the hole '<prefix>' where it belongs; put your own value there, inside the outer quotes." : ""}`,
|
|
22390
|
+
locations: [`Object key: ${key}`, ...locations]
|
|
22391
|
+
};
|
|
22171
22392
|
}
|
|
22172
22393
|
/**
|
|
22173
22394
|
* For `cdkd orphan`: refuse a record whose SURVIVING resource entries carry a
|
|
@@ -22221,11 +22442,11 @@ function malformedOrphanResourcePropertiesRefusalMessage(rawStackName, rawRegion
|
|
|
22221
22442
|
* The orphan set is resolved from the synthesized template before the state is
|
|
22222
22443
|
* loaded, so nothing forces this call any lower.
|
|
22223
22444
|
*/
|
|
22224
|
-
function refuseMalformedResourcePropertiesForOrphan(state, removedLogicalIds, stackName, region) {
|
|
22445
|
+
function refuseMalformedResourcePropertiesForOrphan(state, removedLogicalIds, stackName, region, recovery) {
|
|
22225
22446
|
const removed = new Set(removedLogicalIds);
|
|
22226
22447
|
const unreadable = unreadableResourcePropertyBags(state).filter((id) => !removed.has(id));
|
|
22227
22448
|
if (unreadable.length === 0) return;
|
|
22228
|
-
throw new CdkdError(malformedOrphanResourcePropertiesRefusalMessage(stackName, region, unreadable), STATE_RESOURCES_MALFORMED);
|
|
22449
|
+
throw new CdkdError(malformedOrphanResourcePropertiesRefusalMessage(stackName, region, unreadable, recovery), STATE_RESOURCES_MALFORMED);
|
|
22229
22450
|
}
|
|
22230
22451
|
/**
|
|
22231
22452
|
* The warning a caller of {@link repairMalformedResourcePropertiesForReadOnly}
|
|
@@ -25781,7 +26002,7 @@ function detectUnknownIntrinsicKey(obj) {
|
|
|
25781
26002
|
function buildUnknownIntrinsicError(key) {
|
|
25782
26003
|
const title = `Support intrinsic ${key}`;
|
|
25783
26004
|
const issueUrl = `https://github.com/go-to-k/cdkd/issues/new?title=${encodeURIComponent(title)}&labels=intrinsic-support`;
|
|
25784
|
-
return /* @__PURE__ */ new Error(`Unsupported CloudFormation intrinsic function "${key}": cdkd does not support resolving it yet. Deploying this template would produce a broken value. Please request support by opening an issue: ${issueUrl}`);
|
|
26005
|
+
return /* @__PURE__ */ new Error(`Unsupported CloudFormation intrinsic function "${displayIdent(key)}": cdkd does not support resolving it yet. Deploying this template would produce a broken value. Please request support by opening an issue: ${issueUrl}`);
|
|
25785
26006
|
}
|
|
25786
26007
|
/**
|
|
25787
26008
|
* The same context with the `producerRegions` EVIDENCE removed (issue #2134
|
|
@@ -27277,13 +27498,13 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
27277
27498
|
let referencedNames;
|
|
27278
27499
|
for (const [name, definition] of Object.entries(templateParameters)) {
|
|
27279
27500
|
const paramDef = definition;
|
|
27280
|
-
if (isUnboundTemplateParameter(name, template, userParameters)) throw new Error(`Parameter ${name} is required but no value was provided and no default exists`);
|
|
27501
|
+
if (isUnboundTemplateParameter(name, template, userParameters)) throw new Error(`Parameter ${maskInherited(name)} is required but no value was provided and no default exists`);
|
|
27281
27502
|
if (userParameters && Object.hasOwn(userParameters, name)) {
|
|
27282
27503
|
const userValue = userParameters[name];
|
|
27283
27504
|
if (userValue !== void 0) {
|
|
27284
27505
|
this.refuseCoercedInheritedSecret(name, paramDef, userValue, inheritedSecrets);
|
|
27285
27506
|
parameters[name] = this.coerceParameterValue(userValue, paramDef.Type);
|
|
27286
|
-
this.logger.debug(`Parameter ${name}: using user-provided value ${maskInherited(stringifyParameterForLog(paramDef, this.maskValueLeaves(userValue, inheritedLogContext)))}`);
|
|
27507
|
+
this.logger.debug(`Parameter ${maskInherited(name)}: using user-provided value ${maskInherited(stringifyParameterForLog(paramDef, this.maskValueLeaves(userValue, inheritedLogContext)))}`);
|
|
27287
27508
|
continue;
|
|
27288
27509
|
}
|
|
27289
27510
|
}
|
|
@@ -27291,19 +27512,19 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
27291
27512
|
if (paramDef.Type.startsWith("AWS::SSM::Parameter::Value")) {
|
|
27292
27513
|
referencedNames ??= collectReferencedParameterNames(template);
|
|
27293
27514
|
if (!referencedNames.has(name)) {
|
|
27294
|
-
this.logger.debug(`Parameter ${name}: skipping SSM resolution (not referenced by Resources/Outputs/Conditions)`);
|
|
27515
|
+
this.logger.debug(`Parameter ${maskInherited(name)}: skipping SSM resolution (not referenced by Resources/Outputs/Conditions)`);
|
|
27295
27516
|
continue;
|
|
27296
27517
|
}
|
|
27297
27518
|
const ssmPath = String(paramDef.Default);
|
|
27298
|
-
this.logger.debug(`Parameter ${name}: resolving SSM parameter path ${ssmPath}`);
|
|
27519
|
+
this.logger.debug(`Parameter ${maskInherited(name)}: resolving SSM parameter path ${maskInherited(ssmPath)}`);
|
|
27299
27520
|
const resolved = await this.resolveSSMParameter(ssmPath);
|
|
27300
27521
|
const resolvedType = ssmResolvedValueType(paramDef.Type);
|
|
27301
27522
|
parameters[name] = resolvedType === void 0 ? resolved : this.coerceParameterValue(resolved, resolvedType);
|
|
27302
|
-
this.logger.debug(`Parameter ${name}: resolved SSM value ${maskInherited(stringifyParameterForLog(paramDef, resolved))}`);
|
|
27523
|
+
this.logger.debug(`Parameter ${maskInherited(name)}: resolved SSM value ${maskInherited(stringifyParameterForLog(paramDef, resolved))}`);
|
|
27303
27524
|
continue;
|
|
27304
27525
|
}
|
|
27305
27526
|
parameters[name] = coerceParameterDefault(paramDef.Default, paramDef.Type);
|
|
27306
|
-
this.logger.debug(`Parameter ${name}: using default value ${maskInherited(stringifyParameterForLog(paramDef, paramDef.Default))}`);
|
|
27527
|
+
this.logger.debug(`Parameter ${maskInherited(name)}: using default value ${maskInherited(stringifyParameterForLog(paramDef, paramDef.Default))}`);
|
|
27307
27528
|
continue;
|
|
27308
27529
|
}
|
|
27309
27530
|
}
|
|
@@ -27345,11 +27566,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
27345
27566
|
const inProgress = /* @__PURE__ */ new Set();
|
|
27346
27567
|
const evaluateByName = async (name) => {
|
|
27347
27568
|
if (Object.hasOwn(conditions, name)) return conditions[name];
|
|
27348
|
-
if (inProgress.has(name)) throw new Error(`Circular condition reference detected involving condition "${name}"`);
|
|
27569
|
+
if (inProgress.has(name)) throw new Error(`Circular condition reference detected involving condition "${this.displayMasked(name, maskingContext)}"`);
|
|
27349
27570
|
const bag = templateConditions;
|
|
27350
27571
|
const definition = Object.hasOwn(bag, name) ? bag[name] : void 0;
|
|
27351
27572
|
if (definition === void 0) {
|
|
27352
|
-
this.logger.warn(`Condition ${name} not found in template, assuming false`);
|
|
27573
|
+
this.logger.warn(`Condition ${this.displayMasked(name, maskingContext)} not found in template, assuming false`);
|
|
27353
27574
|
conditions[name] = false;
|
|
27354
27575
|
return false;
|
|
27355
27576
|
}
|
|
@@ -27361,7 +27582,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
27361
27582
|
});
|
|
27362
27583
|
const value = Boolean(result);
|
|
27363
27584
|
conditions[name] = value;
|
|
27364
|
-
this.logger.debug(`Evaluated condition ${name} = ${value}`);
|
|
27585
|
+
this.logger.debug(`Evaluated condition ${this.displayMasked(name, maskingContext)} = ${value}`);
|
|
27365
27586
|
return value;
|
|
27366
27587
|
} finally {
|
|
27367
27588
|
inProgress.delete(name);
|
|
@@ -27474,7 +27695,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
27474
27695
|
configurable: true
|
|
27475
27696
|
});
|
|
27476
27697
|
else resolved[key] = resolvedVal;
|
|
27477
|
-
} else this.logger.debug(`Property ${key} resolved to AWS::NoValue, omitting from object`);
|
|
27698
|
+
} else this.logger.debug(`Property ${this.displayMasked(key, context)} resolved to AWS::NoValue, omitting from object`);
|
|
27478
27699
|
}
|
|
27479
27700
|
return resolved;
|
|
27480
27701
|
}
|
|
@@ -27637,7 +27858,8 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
27637
27858
|
const carriedBefore = inheritedSecretsCarriedBy(userValue, inherited).length;
|
|
27638
27859
|
if (carriedBefore === 0) return;
|
|
27639
27860
|
if (inheritedSecretsCarriedBy(this.coerceParameterValue(userValue, paramDef.Type), inherited).length >= carriedBefore) return;
|
|
27640
|
-
|
|
27861
|
+
const loggedName = this.displayMasked(name);
|
|
27862
|
+
throw markNonRetryable(new IntrinsicResolutionRefusalError(`Nested-stack parameter '${loggedName}' is declared 'Type: ${paramDef.Type}', but the parent stack resolved a SECRET dynamic reference into it. cdkd keeps a resolved secret out of persisted state by rewriting STRING leaves back to their {{resolve:...}} expression; coercing this value to '${paramDef.Type}' destroys the plaintext cdkd would have matched on, so the DECRYPTED secret would be left in the child stack's state.json with nothing to redact it back to. Declare '${loggedName}' as 'Type: String' in the nested stack's template (CDK does this by default for cross-stack references), or stop passing a secret reference into it.`, void 0, "NESTED_STACK_SECRET_PARAMETER_TYPE"));
|
|
27641
27863
|
}
|
|
27642
27864
|
/**
|
|
27643
27865
|
* Resolve Ref intrinsic function
|
|
@@ -27651,14 +27873,14 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
27651
27873
|
const resource = Object.hasOwn(context.resources, logicalId) ? context.resources[logicalId] : void 0;
|
|
27652
27874
|
if (resource) {
|
|
27653
27875
|
const refValue = this.resolveRefValue(logicalId, resource, context);
|
|
27654
|
-
this.logger.debug(`Resolved Ref to resource: ${logicalId} -> ${refValue}`);
|
|
27876
|
+
this.logger.debug(`Resolved Ref to resource: ${this.displayMasked(logicalId, context)} -> ${refValue}`);
|
|
27655
27877
|
return refValue;
|
|
27656
27878
|
}
|
|
27657
27879
|
if (context.parameters && Object.hasOwn(context.parameters, logicalId)) {
|
|
27658
27880
|
const value = context.parameters[logicalId];
|
|
27659
27881
|
const declared = context.template.Parameters;
|
|
27660
27882
|
const paramDef = declared != null && Object.hasOwn(declared, logicalId) ? declared[logicalId] : void 0;
|
|
27661
|
-
this.logger.debug(`Resolved Ref to parameter: ${logicalId} -> ${this.displayMasked(stringifyParameterForLog(paramDef, this.maskValueLeaves(value, context)), context)}`);
|
|
27883
|
+
this.logger.debug(`Resolved Ref to parameter: ${this.displayMasked(logicalId, context)} -> ${this.displayMasked(stringifyParameterForLog(paramDef, this.maskValueLeaves(value, context)), context)}`);
|
|
27662
27884
|
this.recordInheritedParameterSecrets(logicalId, value, context);
|
|
27663
27885
|
return value;
|
|
27664
27886
|
}
|
|
@@ -27758,10 +27980,29 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
27758
27980
|
* nothing to do at this site: the value has ALREADY been masked in state, so
|
|
27759
27981
|
* there is no plaintext to register.
|
|
27760
27982
|
*
|
|
27761
|
-
*
|
|
27762
|
-
*
|
|
27763
|
-
*
|
|
27764
|
-
*
|
|
27983
|
+
* THE SPELLING IS NO LONGER PARSED, and this paragraph used to say the
|
|
27984
|
+
* opposite (corrected in go-to-k/cdkd#3432). It read: "`Ref <LogicalId>
|
|
27985
|
+
* (state key <Key>)` is what `DeployEngine.maskedRecordRemedyFor` partitions
|
|
27986
|
+
* on ... that helper reads the logical id out of it". That was true when it
|
|
27987
|
+
* was written and stopped being true at that helper's round-4 review, which
|
|
27988
|
+
* moved the partition onto the `kind` / `logicalId` FIELDS precisely because
|
|
27989
|
+
* two successive regexes over this rendering each shipped a defect — the
|
|
27990
|
+
* second an `[A-Za-z0-9]+` id class that a HYPHENATED logical id falls out
|
|
27991
|
+
* of. `targetOf` reads `read.logicalId`; no regex touches `display`.
|
|
27992
|
+
*
|
|
27993
|
+
* What the shape IS still load-bearing for is a SENTENCE: the refusal's
|
|
27994
|
+
* `hasRefStateRead` arm tells the reader that "a 'Ref <LogicalId> (state key
|
|
27995
|
+
* <Key>)' entry above is CDKD's own read", so the `Ref ` prefix and the
|
|
27996
|
+
* `(state key ...)` clause have to keep appearing. Sanitizing the ID inside
|
|
27997
|
+
* them leaves both intact.
|
|
27998
|
+
*
|
|
27999
|
+
* THE ID IS SANITIZED, the attribute-read sibling's judgement one branch over
|
|
28000
|
+
* (go-to-k/cdkd#3432). `displayMasked` rather than `displayIdent`: the
|
|
28001
|
+
* builder neither quotes nor bounds, so every legitimate id renders exactly
|
|
28002
|
+
* as it did, and the deliberate collision `intrinsic-ref-state-mask.test.ts`
|
|
28003
|
+
* pins between this rendering and `noteAttributeSecrecy`'s is preserved
|
|
28004
|
+
* because BOTH sites take the same builder. The `logicalId` FIELD stays raw —
|
|
28005
|
+
* it is the routing key and the `--resource <id>=` argument.
|
|
27765
28006
|
*
|
|
27766
28007
|
* `key` is never masked before interpolation because it is not template text:
|
|
27767
28008
|
* it comes from the fixed key lists `cfnRefValueFromPhysicalId` passes
|
|
@@ -27773,7 +28014,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
27773
28014
|
kind: "ref-state-key",
|
|
27774
28015
|
logicalId,
|
|
27775
28016
|
key,
|
|
27776
|
-
display: `Ref ${logicalId} (state key ${key})`
|
|
28017
|
+
display: `Ref ${this.displayMasked(logicalId, context)} (state key ${key})`
|
|
27777
28018
|
});
|
|
27778
28019
|
}
|
|
27779
28020
|
/**
|
|
@@ -27815,7 +28056,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
27815
28056
|
const [rawLogicalId, rawAttributeName] = getAtt;
|
|
27816
28057
|
logicalId = rawLogicalId;
|
|
27817
28058
|
const resolvedAttributeName = await this.resolveValue(rawAttributeName, context);
|
|
27818
|
-
if (typeof resolvedAttributeName !== "string") throw new Error(`Fn::GetAtt attribute name for ${logicalId} must resolve to a string, got ${typeof resolvedAttributeName}: ${stringifyValue(this.maskValueLeaves(resolvedAttributeName, context))}`);
|
|
28059
|
+
if (typeof resolvedAttributeName !== "string") throw new Error(`Fn::GetAtt attribute name for ${this.displayMasked(logicalId, context)} must resolve to a string, got ${typeof resolvedAttributeName}: ${stringifyValue(this.maskValueLeaves(resolvedAttributeName, context))}`);
|
|
27819
28060
|
attributeName = resolvedAttributeName;
|
|
27820
28061
|
} else {
|
|
27821
28062
|
const split = splitGetAttStringForm(getAtt);
|
|
@@ -27824,7 +28065,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
27824
28065
|
attributeName = split.attributeName;
|
|
27825
28066
|
}
|
|
27826
28067
|
const resource = Object.hasOwn(context.resources, logicalId) ? context.resources[logicalId] : void 0;
|
|
27827
|
-
if (!resource) throw markNonRetryable(/* @__PURE__ */ new Error(`Resource ${logicalId} not found for Fn::GetAtt`));
|
|
28068
|
+
if (!resource) throw markNonRetryable(/* @__PURE__ */ new Error(`Resource ${this.displayMasked(logicalId, context)} not found for Fn::GetAtt`));
|
|
27828
28069
|
if (!(resource.resourceType === "AWS::EC2::VPC" && attributeName === "Ipv6CidrBlocks") && resource.attributes !== void 0) {
|
|
27829
28070
|
const storedValue = Object.hasOwn(resource.attributes, attributeName) ? resource.attributes[attributeName] : void 0;
|
|
27830
28071
|
const flatValue = isImpossibleEmptyStoredAttribute(resource.resourceType, attributeName, storedValue) ? void 0 : storedValue;
|
|
@@ -27832,10 +28073,10 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
27832
28073
|
this.rejectPlaceholderArnAttribute(resource, attributeName, flatValue, logicalId, context);
|
|
27833
28074
|
if (resource.resourceType === "AWS::Route53::HostedZone" && attributeName === "NameServers" && typeof flatValue === "string") {
|
|
27834
28075
|
const nameServers = flatValue === "" ? [] : flatValue.split(",");
|
|
27835
|
-
this.logger.debug(`Normalized legacy Fn::GetAtt attribute: ${logicalId}.${this.displayMasked(attributeName, context)} -> ${this.displayMasked(stringifyAttributeForLog(attributeName, this.maskValueLeaves(nameServers, context)), context)}`);
|
|
28076
|
+
this.logger.debug(`Normalized legacy Fn::GetAtt attribute: ${this.displayMasked(logicalId, context)}.${this.displayMasked(attributeName, context)} -> ${this.displayMasked(stringifyAttributeForLog(attributeName, this.maskValueLeaves(nameServers, context)), context)}`);
|
|
27836
28077
|
return this.noteAttributeSecrecy(logicalId, attributeName, nameServers, context);
|
|
27837
28078
|
}
|
|
27838
|
-
this.logger.debug(`Resolved Fn::GetAtt from attributes: ${logicalId}.${this.displayMasked(attributeName, context)} -> ${this.displayMasked(stringifyAttributeForLog(attributeName, this.maskValueLeaves(flatValue, context)), context)}`);
|
|
28079
|
+
this.logger.debug(`Resolved Fn::GetAtt from attributes: ${this.displayMasked(logicalId, context)}.${this.displayMasked(attributeName, context)} -> ${this.displayMasked(stringifyAttributeForLog(attributeName, this.maskValueLeaves(flatValue, context)), context)}`);
|
|
27839
28080
|
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} ${this.displayLeaf(attributeName, context)}`, crossStackSourceKey({ "Fn::GetAtt": getAtt }));
|
|
27840
28081
|
return this.noteAttributeSecrecy(logicalId, attributeName, flatValue, context);
|
|
27841
28082
|
}
|
|
@@ -27848,7 +28089,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
27848
28089
|
break;
|
|
27849
28090
|
}
|
|
27850
28091
|
if (cursor !== void 0) {
|
|
27851
|
-
this.logger.debug(`Resolved Fn::GetAtt from nested attributes: ${logicalId}.${this.displayMasked(attributeName, context)} -> ${this.displayMasked(stringifyAttributeForLog(attributeName, this.maskValueLeaves(cursor, context)), context)}`);
|
|
28092
|
+
this.logger.debug(`Resolved Fn::GetAtt from nested attributes: ${this.displayMasked(logicalId, context)}.${this.displayMasked(attributeName, context)} -> ${this.displayMasked(stringifyAttributeForLog(attributeName, this.maskValueLeaves(cursor, context)), context)}`);
|
|
27852
28093
|
return this.noteAttributeSecrecy(logicalId, attributeName, cursor, context);
|
|
27853
28094
|
}
|
|
27854
28095
|
}
|
|
@@ -27856,10 +28097,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
27856
28097
|
if (resource.resourceType === NESTED_STACK_RESOURCE_TYPE$1 && attributeName.startsWith(NESTED_STACK_OUTPUT_ATTRIBUTE_PREFIX)) {
|
|
27857
28098
|
const declared = Object.keys(resource.attributes ?? {}).filter((k) => k.startsWith(NESTED_STACK_OUTPUT_ATTRIBUTE_PREFIX)).sort().map((k) => this.displayMasked(this.outputNameLogText(k, context), context));
|
|
27858
28099
|
const declaredText = declared.length > 0 ? declared.join(", ") : "(none)";
|
|
27859
|
-
|
|
28100
|
+
const loggedLogicalId = this.displayMasked(logicalId, context);
|
|
28101
|
+
throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${loggedLogicalId}, ${this.displayMasked(attributeName, context)}]: the nested stack '${loggedLogicalId}' declares no output named '${this.displayMasked(this.outputNameLogText(attributeName, context), context)}'. Its outputs are ${this.displayMasked(declaredText, context)}. Check the output name in the nested stack's template, and deploy the child stack again if you have just added it.`));
|
|
27860
28102
|
}
|
|
27861
28103
|
const value = await this.constructGuardedAttribute(resource, attributeName, context, logicalId);
|
|
27862
|
-
this.logger.debug(`Resolved Fn::GetAtt: ${logicalId}.${this.displayMasked(attributeName, context)} -> ${this.displayMasked(stringifyAttributeForLog(attributeName, this.maskValueLeaves(value, context)), context)}`);
|
|
28104
|
+
this.logger.debug(`Resolved Fn::GetAtt: ${this.displayMasked(logicalId, context)}.${this.displayMasked(attributeName, context)} -> ${this.displayMasked(stringifyAttributeForLog(attributeName, this.maskValueLeaves(value, context)), context)}`);
|
|
27863
28105
|
return value;
|
|
27864
28106
|
}
|
|
27865
28107
|
/**
|
|
@@ -27915,7 +28157,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
27915
28157
|
kind: "attribute",
|
|
27916
28158
|
logicalId,
|
|
27917
28159
|
key: maskedAttributeName,
|
|
27918
|
-
display: `${logicalId}.${maskedAttributeName}`
|
|
28160
|
+
display: `${this.displayMasked(logicalId, context)}.${maskedAttributeName}`
|
|
27919
28161
|
});
|
|
27920
28162
|
}
|
|
27921
28163
|
return value;
|
|
@@ -27952,7 +28194,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
27952
28194
|
rejectPlaceholderArnAttribute(resource, attributeName, value, logicalId, context) {
|
|
27953
28195
|
if (!REF_RETURNS_ARN_FROM_STATE.get(resource.resourceType)?.includes(attributeName)) return;
|
|
27954
28196
|
if (typeof value !== "string" || !isPlaceholderArn(value)) return;
|
|
27955
|
-
throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.displayMasked(attributeName, context)}] for ${resource.resourceType}: the recorded value "${stringifyValue(this.maskValueLeaves(value, context))}" is a placeholder written by a cdkd version older than issue #1681 — its region and account fields are literal wildcards, so it is not a usable ARN. Deploy the stack again so the resource's next update heals the record (cdkd now records the real ARN), or re-import the resource.`));
|
|
28197
|
+
throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${this.displayMasked(logicalId, context)}, ${this.displayMasked(attributeName, context)}] for ${resource.resourceType}: the recorded value "${stringifyValue(this.maskValueLeaves(value, context))}" is a placeholder written by a cdkd version older than issue #1681 — its region and account fields are literal wildcards, so it is not a usable ARN. Deploy the stack again so the resource's next update heals the record (cdkd now records the real ARN), or re-import the resource.`));
|
|
27956
28198
|
}
|
|
27957
28199
|
/**
|
|
27958
28200
|
* Construct resource attribute value based on resource type, refusing to
|
|
@@ -27997,7 +28239,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
27997
28239
|
async constructGuardedAttribute(resource, attributeName, context, logicalId) {
|
|
27998
28240
|
const accountInfo = await getAccountInfo(this.resolverRegion);
|
|
27999
28241
|
const value = await this.constructAttribute(resource, attributeName, context, logicalId, accountInfo);
|
|
28000
|
-
if (accountInfo.fabricated && embedsAccountId(value, accountInfo.accountId)) throw new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.displayMasked(attributeName, context)}] for ${resource.resourceType}: STS did not report this deploy's account id, so cdkd would build the value from the placeholder account ${accountInfo.accountId} — structurally valid, naming a different account, and indistinguishable downstream from a real one. Fix the AWS credentials (or set AWS_ACCOUNT_ID to this deploy's account) and deploy again.`);
|
|
28242
|
+
if (accountInfo.fabricated && embedsAccountId(value, accountInfo.accountId)) throw new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${this.displayMasked(logicalId, context)}, ${this.displayMasked(attributeName, context)}] for ${resource.resourceType}: STS did not report this deploy's account id, so cdkd would build the value from the placeholder account ${accountInfo.accountId} — structurally valid, naming a different account, and indistinguishable downstream from a real one. Fix the AWS credentials (or set AWS_ACCOUNT_ID to this deploy's account) and deploy again.`);
|
|
28001
28243
|
return value;
|
|
28002
28244
|
}
|
|
28003
28245
|
/**
|
|
@@ -28059,7 +28301,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
28059
28301
|
return [];
|
|
28060
28302
|
}
|
|
28061
28303
|
case "DefaultSecurityGroup": {
|
|
28062
|
-
if (!/^vpc-[0-9a-f]+$/.test(physicalId)) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, DefaultSecurityGroup] for AWS::EC2::VPC: the state record's physical id "${this.displayMasked(physicalId, context).slice(0, 64)}" is not a VPC id (vpc-<hex>), so cdkd will not use it as an EC2 filter value. Repair the record (cdkd import, or re-create the VPC) and deploy again.`));
|
|
28304
|
+
if (!/^vpc-[0-9a-f]+$/.test(physicalId)) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${this.displayMasked(logicalId, context)}, DefaultSecurityGroup] for AWS::EC2::VPC: the state record's physical id "${this.displayMasked(physicalId, context).slice(0, 64)}" is not a VPC id (vpc-<hex>), so cdkd will not use it as an EC2 filter value. Repair the record (cdkd import, or re-create the VPC) and deploy again.`));
|
|
28063
28305
|
const cachedGroupId = cachedVpcDefaultSecurityGroups[physicalId];
|
|
28064
28306
|
if (cachedGroupId !== void 0) return cachedGroupId;
|
|
28065
28307
|
let groupId;
|
|
@@ -28274,7 +28516,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
28274
28516
|
if (resourceType === "AWS::EC2::SecurityGroup") switch (attributeName) {
|
|
28275
28517
|
case "GroupId": return physicalId;
|
|
28276
28518
|
case "VpcId": {
|
|
28277
|
-
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 "${this.displayMasked(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.`));
|
|
28519
|
+
if (!/^sg-[0-9a-f]+$/.test(physicalId)) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${this.displayMasked(logicalId, context)}, VpcId] for AWS::EC2::SecurityGroup: the state record's physical id "${this.displayMasked(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.`));
|
|
28278
28520
|
const cachedVpcId = cachedSecurityGroupVpcIds[physicalId];
|
|
28279
28521
|
if (cachedVpcId !== void 0) return cachedVpcId;
|
|
28280
28522
|
let vpcId;
|
|
@@ -28391,7 +28633,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
28391
28633
|
if (resourceType === "AWS::CloudFront::Distribution") switch (attributeName) {
|
|
28392
28634
|
case "Id": return physicalId;
|
|
28393
28635
|
case "DomainName": {
|
|
28394
|
-
if (!/^[A-Z0-9]+$/.test(physicalId)) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, DomainName] for AWS::CloudFront::Distribution: the state record's physical id "${this.displayMasked(physicalId, context).slice(0, 64)}" is not a distribution id (upper-case alphanumerics), so cdkd will not look it up. Repair the record (cdkd import, or re-create the distribution) and deploy again.`));
|
|
28636
|
+
if (!/^[A-Z0-9]+$/.test(physicalId)) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${this.displayMasked(logicalId, context)}, DomainName] for AWS::CloudFront::Distribution: the state record's physical id "${this.displayMasked(physicalId, context).slice(0, 64)}" is not a distribution id (upper-case alphanumerics), so cdkd will not look it up. Repair the record (cdkd import, or re-create the distribution) and deploy again.`));
|
|
28395
28637
|
const cachedDomainName = cachedCloudFrontDomainNames[physicalId];
|
|
28396
28638
|
if (cachedDomainName !== void 0) return cachedDomainName;
|
|
28397
28639
|
let domainName;
|
|
@@ -28425,7 +28667,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
28425
28667
|
default: return this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, physicalId, context);
|
|
28426
28668
|
}
|
|
28427
28669
|
if (resourceType === "AWS::RDS::DBProxy" || resourceType === "AWS::RDS::DBProxyEndpoint") {
|
|
28428
|
-
if (attributeName === "VpcId") throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.displayMasked(attributeName, context)}] for ${resourceType}: the state record holds no VpcId for it (the read-back that would have recorded it reported none), and the physical id "${this.displayMasked(physicalId, context)}" is a name, not a VPC id, so cdkd refuses to substitute it. Update the resource so its next deploy records the value, or reference the VPC directly.`));
|
|
28670
|
+
if (attributeName === "VpcId") throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${this.displayMasked(logicalId, context)}, ${this.displayMasked(attributeName, context)}] for ${resourceType}: the state record holds no VpcId for it (the read-back that would have recorded it reported none), and the physical id "${this.displayMasked(physicalId, context)}" is a name, not a VPC id, so cdkd refuses to substitute it. Update the resource so its next deploy records the value, or reference the VPC directly.`));
|
|
28429
28671
|
return this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, physicalId, context);
|
|
28430
28672
|
}
|
|
28431
28673
|
return this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, physicalId, context);
|
|
@@ -28484,7 +28726,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
28484
28726
|
*/
|
|
28485
28727
|
refuseUnservedAttribute(site) {
|
|
28486
28728
|
const { logicalId, attributeName, resourceType, physicalId, context, observed, remedy } = site;
|
|
28487
|
-
throw new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.displayMasked(attributeName, context)}] for ${resourceType}: ${observed}. The physical id "${this.displayMasked(physicalId, context)}" is not a usable ${this.displayMasked(attributeName, context)}, so cdkd refuses to substitute it. ${remedy}`);
|
|
28729
|
+
throw new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${this.displayMasked(logicalId, context)}, ${this.displayMasked(attributeName, context)}] for ${resourceType}: ${observed}. The physical id "${this.displayMasked(physicalId, context)}" is not a usable ${this.displayMasked(attributeName, context)}, so cdkd refuses to substitute it. ${remedy}`);
|
|
28488
28730
|
}
|
|
28489
28731
|
/**
|
|
28490
28732
|
* The `observed` clause of {@link refuseUnservedAttribute} for a live read
|
|
@@ -28531,9 +28773,9 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
28531
28773
|
const expectsUrlShape = attributeName.endsWith("Url") && !/^https?:\/\//.test(physicalId);
|
|
28532
28774
|
if (expectsArnShape || expectsUrlShape) {
|
|
28533
28775
|
const expectedShape = expectsArnShape ? "an ARN (arn:...)" : "a URL (http(s)://...)";
|
|
28534
|
-
throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.displayMasked(attributeName, context)}] for ${resourceType}: attributes are not enriched for this resource type, and the physical ID fallback "${this.displayMasked(physicalId, context)}" is not ${expectedShape}. CloudFormation would return a different value here, so falling back to the physical ID would silently produce a wrong value (e.g. in stack Outputs). Avoid this Fn::GetAtt, or file an issue at https://github.com/go-to-k/cdkd/issues so cdkd can enrich ${resourceType}.${this.displayMasked(attributeName, context)}.`));
|
|
28776
|
+
throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${this.displayMasked(logicalId, context)}, ${this.displayMasked(attributeName, context)}] for ${resourceType}: attributes are not enriched for this resource type, and the physical ID fallback "${this.displayMasked(physicalId, context)}" is not ${expectedShape}. CloudFormation would return a different value here, so falling back to the physical ID would silently produce a wrong value (e.g. in stack Outputs). Avoid this Fn::GetAtt, or file an issue at https://github.com/go-to-k/cdkd/issues so cdkd can enrich ${resourceType}.${this.displayMasked(attributeName, context)}.`));
|
|
28535
28777
|
}
|
|
28536
|
-
if (this.strictGetAtt) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.displayMasked(attributeName, context)}] for ${resourceType}: attributes are not enriched for this resource type, and --strict-getatt rejects the physical ID fallback "${this.displayMasked(physicalId, context)}" (which may not be the value CloudFormation would return). Drop --strict-getatt to fall back with a warning, avoid this Fn::GetAtt, or file an issue at https://github.com/go-to-k/cdkd/issues so cdkd can enrich ${resourceType}.${this.displayMasked(attributeName, context)}.`));
|
|
28778
|
+
if (this.strictGetAtt) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${this.displayMasked(logicalId, context)}, ${this.displayMasked(attributeName, context)}] for ${resourceType}: attributes are not enriched for this resource type, and --strict-getatt rejects the physical ID fallback "${this.displayMasked(physicalId, context)}" (which may not be the value CloudFormation would return). Drop --strict-getatt to fall back with a warning, avoid this Fn::GetAtt, or file an issue at https://github.com/go-to-k/cdkd/issues so cdkd can enrich ${resourceType}.${this.displayMasked(attributeName, context)}.`));
|
|
28537
28779
|
this.physicalIdFallbackCount++;
|
|
28538
28780
|
this.logger.warn(`Unknown attribute ${this.displayMasked(attributeName, context)} for resource type ${resourceType}, returning physical ID`);
|
|
28539
28781
|
return physicalId;
|
|
@@ -29009,7 +29251,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
29009
29251
|
const resolvedValue = await this.resolveValue(value, context);
|
|
29010
29252
|
if (typeof resolvedValue !== "string") {
|
|
29011
29253
|
const source = this.describeSplitValueSource(value);
|
|
29012
|
-
const sourceClause = source ? ` (from ${source.label})` : "";
|
|
29254
|
+
const sourceClause = source ? ` (from ${this.displayMasked(source.label, context)})` : "";
|
|
29013
29255
|
if (Array.isArray(resolvedValue)) {
|
|
29014
29256
|
const remedy = source?.kind === "ref" ? "A list-typed parameter — any List<...> type (List<AWS::EC2::Subnet::Id>, List<Number>, …) or CommaDelimitedList — is already a list." : source?.kind === "getatt" ? "A list-valued Fn::GetAtt (for example AWS::Route53::HostedZone.NameServers or AWS::EC2::VPC.Ipv6CidrBlocks) already returns a list. If you wrote the Fn::Split as a workaround for cdkd resolving that attribute to a comma-delimited string, that bug is fixed (PR #1868) and the workaround is no longer needed." : "Several intrinsics already return a list — among them a list-valued Fn::GetAtt, a Ref to a list-typed parameter (any List<...> type or CommaDelimitedList), Fn::GetAZs, Fn::Cidr, and Fn::Split itself.";
|
|
29015
29257
|
throw markNonRetryable(new IntrinsicResolutionRefusalError(`Fn::Split: the value to split${sourceClause} is ALREADY a list (an array of ${resolvedValue.length} item${resolvedValue.length === 1 ? "" : "s"}), not a string. CloudFormation rejects Fn::Split over a list too, so this template is not valid CloudFormation either. Remove the Fn::Split and use the value directly. ${remedy}`));
|
|
@@ -29030,12 +29272,12 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
29030
29272
|
async resolveIf(ifArgs, context) {
|
|
29031
29273
|
const [conditionName, valueIfTrue, valueIfFalse] = ifArgs;
|
|
29032
29274
|
if (!context.conditions || !Object.hasOwn(context.conditions, conditionName)) {
|
|
29033
|
-
this.logger.warn(`Condition ${conditionName} not found in context, assuming false`);
|
|
29275
|
+
this.logger.warn(`Condition ${this.displayMasked(String(conditionName), context)} not found in context, assuming false`);
|
|
29034
29276
|
return await this.resolveValue(valueIfFalse, context);
|
|
29035
29277
|
}
|
|
29036
29278
|
const conditionValue = context.conditions[conditionName];
|
|
29037
29279
|
const selectedValue = conditionValue ? valueIfTrue : valueIfFalse;
|
|
29038
|
-
this.logger.debug(`Resolved Fn::If: condition ${conditionName} = ${conditionValue}, selected ${conditionValue ? "true" : "false"} branch`);
|
|
29280
|
+
this.logger.debug(`Resolved Fn::If: condition ${this.displayMasked(String(conditionName), context)} = ${conditionValue}, selected ${conditionValue ? "true" : "false"} branch`);
|
|
29039
29281
|
return await this.resolveValue(selectedValue, context);
|
|
29040
29282
|
}
|
|
29041
29283
|
/**
|
|
@@ -29066,7 +29308,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
29066
29308
|
async resolveConditionReference(conditionName, context) {
|
|
29067
29309
|
if (context.conditionResolver) return await context.conditionResolver(conditionName);
|
|
29068
29310
|
if (context.conditions && Object.hasOwn(context.conditions, conditionName)) return context.conditions[conditionName];
|
|
29069
|
-
this.logger.warn(`Condition ${conditionName} not found in context, assuming false`);
|
|
29311
|
+
this.logger.warn(`Condition ${this.displayMasked(conditionName, context)} not found in context, assuming false`);
|
|
29070
29312
|
return false;
|
|
29071
29313
|
}
|
|
29072
29314
|
/**
|
|
@@ -30003,11 +30245,14 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
30003
30245
|
*
|
|
30004
30246
|
* Reachable from ONE place, {@link maskThenStripThenMask}, which is itself
|
|
30005
30247
|
* reachable only from {@link displayMasked}. That containment is the whole
|
|
30006
|
-
* mechanism
|
|
30007
|
-
*
|
|
30008
|
-
*
|
|
30009
|
-
*
|
|
30010
|
-
*
|
|
30248
|
+
* mechanism. It used to be enforced by an AST walk that resolved an
|
|
30249
|
+
* interpolated identifier to its declaration; go-to-k/cdkd#3435 deleted that
|
|
30250
|
+
* checker, and what holds the containment now is
|
|
30251
|
+
* `tests/unit/deployment/resolver-display-masked-population.test.ts`, which
|
|
30252
|
+
* asserts the exact reference COUNTS from the AST (one caller each) and
|
|
30253
|
+
* refuses a direct `${this.<raw masker>(...)}` interpolation by line rule.
|
|
30254
|
+
* Do not call this from a new site: a binding of it interpolated LATER is
|
|
30255
|
+
* the shape neither survivor can see.
|
|
30011
30256
|
*
|
|
30012
30257
|
* ## What the masking itself does
|
|
30013
30258
|
*
|
|
@@ -30102,7 +30347,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
30102
30347
|
* with the twin's positions, so the line gives both up for `***`: never
|
|
30103
30348
|
* more text than either mask alone would print. Returns the text BEFORE the
|
|
30104
30349
|
* needle mask; each log line wraps it in `displayMasked` itself, so the
|
|
30105
|
-
*
|
|
30350
|
+
* sanitization happens at the render rather than here.
|
|
30106
30351
|
*/
|
|
30107
30352
|
logTwinText(result, twin, context) {
|
|
30108
30353
|
return twin !== result && this.maskNeedlesForLog(result, context) !== result ? "***" : twin;
|
|
@@ -30450,9 +30695,12 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
30450
30695
|
* ONE CALLER, {@link displayMasked}, since go-to-k/cdkd#3426. It is most of
|
|
30451
30696
|
* the answer and reads like all of it — it omits `displaySafe`, and
|
|
30452
30697
|
* therefore `U+2028` / `U+2029` and the bidi overrides — so a site reaching
|
|
30453
|
-
* for it directly is the near-miss back onto the treadmill.
|
|
30454
|
-
* `
|
|
30455
|
-
*
|
|
30698
|
+
* for it directly is the near-miss back onto the treadmill. A DIRECT
|
|
30699
|
+
* `${this.maskThenStripThenMask(...)}` interpolation is refused by
|
|
30700
|
+
* `resolver-display-masked-population.test.ts`'s line rule; a BINDING of it
|
|
30701
|
+
* rendered later is not seen by anything since go-to-k/cdkd#3435 deleted the
|
|
30702
|
+
* AST checker, which is why the one-caller containment above is the control
|
|
30703
|
+
* that matters.
|
|
30456
30704
|
*
|
|
30457
30705
|
* THE BOUND, since this file's job is to state them: this covers a needle
|
|
30458
30706
|
* split by a character `stripControlChars` removes. A needle split by
|
|
@@ -30500,15 +30748,19 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
30500
30748
|
*
|
|
30501
30749
|
* ## What keeps it closed
|
|
30502
30750
|
*
|
|
30503
|
-
* `
|
|
30504
|
-
*
|
|
30505
|
-
*
|
|
30506
|
-
*
|
|
30507
|
-
*
|
|
30508
|
-
*
|
|
30509
|
-
*
|
|
30510
|
-
*
|
|
30511
|
-
*
|
|
30751
|
+
* `tests/unit/deployment/resolver-display-masked-population.test.ts`, which
|
|
30752
|
+
* asserts from the AST that `maskSecretsRaw` is referenced only inside
|
|
30753
|
+
* `maskThenStripThenMask` and that helper only inside this builder — exact
|
|
30754
|
+
* reference counts, no inference — and refuses a direct
|
|
30755
|
+
* `${this.<raw masker>(...)}` interpolation by line rule.
|
|
30756
|
+
*
|
|
30757
|
+
* An AST walk that ALSO resolved an interpolated identifier to its
|
|
30758
|
+
* declaration used to cover the BINDING shape. go-to-k/cdkd#3435 deleted it
|
|
30759
|
+
* as high-maintenance tooling (23 commits across it and its suite in nine
|
|
30760
|
+
* days; a widening attempted one PR earlier produced nine defects inside
|
|
30761
|
+
* itself and was withdrawn), so that shape is now held by the containment
|
|
30762
|
+
* above — there is no bare masker to bind, because nothing outside this
|
|
30763
|
+
* builder may call one.
|
|
30512
30764
|
*
|
|
30513
30765
|
* KNOWN BOUND, stated rather than implied away: a site carrying an
|
|
30514
30766
|
* exclusion marker is exempt from that walk, because the marker answers the
|
|
@@ -30533,9 +30785,9 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
30533
30785
|
* Trojan-Source bidi overrides. That composition is what the `physicalId`
|
|
30534
30786
|
* renders here already spelled by hand; this names it, and they now share it.
|
|
30535
30787
|
*
|
|
30536
|
-
* It is
|
|
30537
|
-
*
|
|
30538
|
-
*
|
|
30788
|
+
* It is the ONE sanctioned exit from the masking machinery, which is a
|
|
30789
|
+
* SECURITY decision and a sound one: every path through it passes the
|
|
30790
|
+
* needle-and-twin mask, twice, so it is strictly stronger than the bare
|
|
30539
30791
|
* masker it replaced and cannot be weaker at any input.
|
|
30540
30792
|
*/
|
|
30541
30793
|
displayMasked(value, context) {
|
|
@@ -32635,7 +32887,7 @@ var CloudControlProvider = class {
|
|
|
32635
32887
|
const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
|
|
32636
32888
|
if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
|
|
32637
32889
|
this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
|
|
32638
|
-
const { ASGProvider } = await import("./asg-provider-
|
|
32890
|
+
const { ASGProvider } = await import("./asg-provider-BuEvX2d9.js").then((n) => n.n);
|
|
32639
32891
|
const asgProvider = new ASGProvider();
|
|
32640
32892
|
return withIndeterminateGuard(await asgProvider.delete(logicalId, physicalId, resourceType, _properties, context), indeterminateGuard);
|
|
32641
32893
|
}
|
|
@@ -43271,11 +43523,38 @@ var DeployEngine = class DeployEngine {
|
|
|
43271
43523
|
* cdkd never EXECUTES this string — it is advice in an error message — so
|
|
43272
43524
|
* this is about the pasted command being correct, not about injection into
|
|
43273
43525
|
* cdkd itself (issue #2847 round-5 review).
|
|
43526
|
+
*
|
|
43527
|
+
* SINCE go-to-k/cdkd#3435 no id reaching this arm can contain a `'`:
|
|
43528
|
+
* `isPasteableIdent` admits `[A-Za-z0-9][A-Za-z0-9~_.-]*` only, and the
|
|
43529
|
+
* command is not emitted for anything else. Kept anyway rather than
|
|
43530
|
+
* deleted — dropping a guard on the strength of a NEW guard one line up is
|
|
43531
|
+
* how the pair later comes apart, and this one costs a `replaceAll` on a
|
|
43532
|
+
* short string.
|
|
43274
43533
|
*/
|
|
43275
43534
|
const quoteSafe = (id) => id.replaceAll("'", `'\\''`);
|
|
43535
|
+
/**
|
|
43536
|
+
* A logical id rendered as a NAME rather than as a command argument
|
|
43537
|
+
* (go-to-k/cdkd#3435 security round).
|
|
43538
|
+
*
|
|
43539
|
+
* Every sentence below is joined into a `ProvisioningError` message that
|
|
43540
|
+
* `handleError` prints at DEFAULT verbosity, and neither `formatError` nor
|
|
43541
|
+
* the logger sanitizes `error.message` — they cover a `cause` and the extra
|
|
43542
|
+
* ARGS respectively. So a `Resources` KEY carrying `ESC[2K` + CR reached the
|
|
43543
|
+
* terminal raw, which is the same class this PR closed at 18 resolver
|
|
43544
|
+
* renders and missed one module out.
|
|
43545
|
+
*/
|
|
43546
|
+
const shown = (id) => displayIdent(id);
|
|
43547
|
+
/** CloudFormation's own logical-id length bound. */
|
|
43548
|
+
const CFN_LOGICAL_ID_MAX_LENGTH = 255;
|
|
43276
43549
|
const parts = [];
|
|
43277
|
-
if (localTargets.length > 0)
|
|
43278
|
-
|
|
43550
|
+
if (localTargets.length > 0) {
|
|
43551
|
+
const pasteableHere = (id) => isPasteableIdent(id) && id.length <= CFN_LOGICAL_ID_MAX_LENGTH;
|
|
43552
|
+
const pasteable = localTargets.filter(pasteableHere);
|
|
43553
|
+
const withheld = localTargets.filter((id) => !pasteableHere(id));
|
|
43554
|
+
if (pasteable.length > 0) parts.push(`Re-import the record that HOLDS the mask: ` + pasteable.map((id) => `'cdkd import <stack> --resource ${quoteSafe(id)}=<physicalId> --force'`).join(", ") + `.`);
|
|
43555
|
+
if (withheld.length > 0) parts.push(`Re-import the record that HOLDS the mask for ${withheld.map(shown).join(", ")}, but the command is withheld: that is not a plain CloudFormation logical id, so a pasted 'cdkd import <stack> --resource <id>=<physicalId> --force' could be reshaped by the shell or name a different resource. Read the id from 'cdkd state show' and quote it yourself.`);
|
|
43556
|
+
}
|
|
43557
|
+
if (unclearableTargets.length > 0) parts.push(`Do NOT re-import ${unclearableTargets.map(shown).join(", ")}: a custom resource's import records no attributes, so the masked bag is carried forward unchanged and the refusal repeats. Cause (1) above is the one that applies to it.`);
|
|
43279
43558
|
if (foreignReads.length > 0) {
|
|
43280
43559
|
const subject = reads.length === 1 ? "The read above resolves" : foreignReads.length === 1 ? "One of the reads above resolves" : "Some of the reads above resolve";
|
|
43281
43560
|
parts.push(`${subject} through ANOTHER stack (an Fn::ImportValue, an Fn::GetStackOutput, or a nested stack's Outputs), whose masked record lives in that stack's state — re-importing anything in this stack cannot clear it; act on the producer stack instead.`);
|
|
@@ -45702,4 +45981,4 @@ var DeployEngine = class DeployEngine {
|
|
|
45702
45981
|
|
|
45703
45982
|
//#endregion
|
|
45704
45983
|
export { secretBearing as $, SynthesisError as $i, recordMaskOnlyValue as $n, AssetManifestLoader as $r, s3BucketArn as $t, extractDeploymentEventError as A, getAwsClients as Ai, refuseMalformedState as An, BOOTSTRAP_MARKER_PREFIX as Ar, isImpossibleEmptyStoredAttribute as At, toCfnInteger as B, IntrinsicResolutionRefusalError as Bi, STATE_SOURCED_BASELINE_RULES as Bn, buildDockerImage as Br, coerceCfnBoolean as Bt, buildFinalSnapshotIdentifier as C, canonicalizeRegion as Ci, producerRecordKey as Cn, stringifyValue as Cr, isTerminationProtectionPropagationError as Ct, refusesFinalSnapshot as D, clearBucketRegionCache as Di, refuseMalformedOutputsForDestroy as Dn, loadPublishableAssetManifest as Dr, cfnRefValueFromPhysicalId as Dt, isFinalSnapshotError as E, processStackMessages as Ei, refuseMalformedOutputs as En, createAssetRedirectResolver as Er, carriesFetchableDynamicReference as Et, isStatefulRecreateTargetSync as F, ConfigError as Fi, findSilentDropProperties as Fn, parseBootstrapMarker as Fr, isSettledInstanceState as Ft, gray as G, NestedStackChildDirectDestroyError as Gi, createSecretMasker as Gn, formatDockerLoginError as Gr, readConfigString as Gt, formatResourceLine as H, LocalStartServiceError as Hi, STATE_SOURCED_READBACK_RULES as Hn, describeDockerExecFailure as Hr, configBooleanRefusal as Ht, renderStatefulReason as I, CrossAccountSecretRefusalError as Ii, describeTypeWithThrottleRetry as In, readBootstrapMarkerBody as Ir, WAFv2WebACLProvider as It, yellow as J, ResourceTimeoutError as Ji, identityKeyFor as Jn, redactDockerArgvValues as Jr, requireConfigObject as Jt, green as K, PartialFailureError as Ki, dynamicReferenceTokens as Kn, getDockerCmd as Kr, replayWarn as Kt, WARM_THROUGHPUT_MEMBERS as L, DependencyError as Li, withRetry as Ln, validateAssetBucketName as Lr, normalizeAwsTagsToCfn as Lt, planOrphanAdoption as M, setAwsClients as Mi, repairMalformedResourcePropertiesForReadOnly as Mn, ensureAssetStorage as Mr, isUnboundTemplateParameter as Mt, MULTI_REGION_RECREATE_BLOCKED_TYPES as N, AssetError as Ni, repairMalformedResourcesForReadOnly as Nn, getBootstrapMarkerKey as Nr, parameterTypeMayLoseSecretIdentity as Nt, unsupportedFinalSnapshotError as O, resolveBucketRegion as Oi, refuseMalformedResourcePropertiesForOrphan as On, rewriteTemplateAssetReferences as Or, coerceParameterTypedValue as Ot, isStatefulRecreateTargetForReplace as P, CdkdError as Pi, findActionableSilentDrops as Pn, isCrossRegionRedirect as Pr, refStateLookupFromResource as Pt, isExportAliasCollision as Q, StateError as Qi, maskSecretsInText as Qn, stripControlChars as Qr, withSharedDrainBudget as Qt, coerceWarmThroughput as R, DeployCancelledError as Ri, DagBuilder as Rn, validateContainerRepoName as Rr, resolveExplicitPhysicalId as Rt, PRE_DELETE_SNAPSHOT_TYPES as S, PARTITION_TABLE as Si, malformedStateRefusalMessage as Sn, AssetPublisher as Sr, disableInstanceApiTermination as St, createPreDeleteFinalSnapshot as T, AssemblyReader as Ti, refuseMalformedNestedChildOutputs as Tn, buildAssetRedirectMap as Tr, carriesDynamicReference as Tt, bold as U, LockError as Ui, TEMPLATE_SOURCED_RULES as Un, describeDockerFailure as Ur, configIntegerRefusal as Ut, toFiniteNumber as V, LocalInvokeBuildError as Vi, STATE_SOURCED_CROSS_GENERATION_RULES as Vn, describeDockerCapturedOutput as Vr, coerceCfnInteger as Vt, cyan as W, MalformedProducerRecordRefusalError as Wi, carriesSecretMask as Wn, dockerSpawnEnvWithSensitive as Wr, configStringRefusal as Wt, collectPublishedOutputNames as X, StackHasActiveImportsError as Xi, markSameGenerationBag as Xn, runDockerStreaming as Xr, classifyReplaySecretRegion as Xt, collectDeclaredOutputNames as Y, ResourceUpdateNotSupportedError as Yi, isSingleDynamicReferenceToken as Yn, runDockerForeground as Yr, requireConfigString as Yt, exportAliasCollisionScrubWarning as Z, StackTerminationProtectionError as Zi, maskSecretsInError as Zn, escapeRegExp$1 as Zr, producerRegionsFromState as Zt, isSecretBearingReferenceString as _, findLargeInlineResources as _i, malformedOutputsRefusalMessage as _n, importableOutputs as _r, isWaitAbandonedError as _t, DeploymentEventsStore as a, isRetryableTransientError as aa, resolveApp as ai, AWS_CREDENTIAL_ENV_KEYS as an, rebuildClientForBucketRegion as ar, clearOnUpdateRemoval as at, bindingSkippedOutputs as b, decodeListingKey as bi, malformedResourcePropertiesWarning as bn, orphansCarriedFrom as br, deleteIndeterminateGuards as bt, replayFailedOperations as c, markNonRetryable as ca, resolveSkipPrefix as ci, envCredentialsAreAssumedRole as cn, buildLockContentionMessage as cr, createMaskedRetryLogger as ct, updatePartialReason as d, describeAwsFailure as da, resolveUseCdkBootstrapAssets as di, STATE_RESOURCES_MALFORMED as dn, shellQuote as dr, beginCommandInterruptScope as dt, formatError as ea, getDockerImageBySourceHash as ei, s3BucketDomainName as en, recoverMaskedOutput as er, secretBearingStateKeyWarning as et, withResourceDeadline as f, safeStringify$1 as fa, stateBucketExistenceConfirmed as fi, hasReadableOutputs as fn, CUSTOM_RESOURCE_RESPONSE_PREFIX as fr, endCommandInterruptScope as ft, bagHoldsSecretExpression as g, MIGRATE_TMP_PREFIX as gi, malformedLocalOutputsWarning as gn, importableOutputKeys as gr, CloudControlProvider as gt, computeImplicitDeleteEdges as h, CFN_TEMPLATE_URL_LIMIT as hi, malformedExportSourceWarning as hn, hasReadableExportSet as hr, startInterruptWatch as ht, DeploymentEventsReader as i, isMarkedNonRetryable as ia, getLegacyStateBucketName as ii, applyRoleArnIfSet as in, S3StateBackend as ir, collectInlinePolicyNamesManagedBySiblings as it, makeSiblingClaimReader as j, resetAwsClients as ji, repairMalformedOutputsForReadOnly as jn, assertAssetBucketRegion as jr, isNamelessDynamicReferenceError as jt, makeCanonicalizePropertiesFn as k, AwsClients as ki, refuseMalformedResourcesForDestroy as kn, AssetModeResolver as kr, getAccountInfo as kt, replayRollback as l, markRedactedCause as la, resolveStateBucketWithDefault as li, DiffCalculator as ln, forceQuitRecoveryClause as lr, maskDeep as lt, IMPLICIT_DELETE_DEPENDENCIES as m, CFN_TEMPLATE_BODY_LIMIT as mi, malformedExportNamesWarning as mn, exportNamesCarriedFrom as mr, isInterruptedWaitError as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, normalizeAwsError as na, synthesisStatusMessage as ni, s3BucketRegionalDomainName as nn, scrubResourceRecord as nr, getCurrentResourceSecrets as nt, planFailedOps as o, isThrottlingError as oa, resolveAutoAssetStorage as oi, applyCallerIdentityCredentials as on, UNREPRODUCIBLE_LOCK_CLAUSE as or, ProviderRegistry as ot, maskingRetryLogger as p, __exportAll as pa, warnDeprecatedNoPrefixCliFlag as pi, hasReadableResources as pn, DEFAULT_STATE_PREFIX as pr, interruptWatchListenerCount as pt, red as q, ProvisioningError as qi, errorCauseChain as qn, partitionSensitiveEnv as qr, requireConfigArray as qt, DeployEngine as r, withErrorHandling as ra, getDefaultStateBucketName as ri, s3BucketWebsiteUrl as rn, LockManager as rr, IAMRoleProvider as rt, planRollback as s, isTransientServerError as sa, resolveCaptureObservedState as si, callerEnvCredentials as sn, buildForceUnlockCommand as sr, wouldReturnToSdkProvider as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, isCdkdError as ta, Synthesizer as ti, s3BucketDualStackDomainName as tn, redactSecretsForState as tr, secretSafeKeyDisplay as tt, updatePartialMessage as u, retryClassificationText as ua, resolveStateBucketWithDefaultAndSource as ui, INTRINSIC_KEYS as un, formatLockExpiry as ur, maskerOrIdentity as ut, keptWholeReasonText as v, uploadCfnTemplate as vi, malformedOutputsWarning as vn, isReadableBag as vr, slowCcOperationTimeoutMs as vt, ccRoutedFinalSnapshotError as w, derivePartitionAndUrlSuffix as wi, refuseDivergentRecordRegionForDestroy as wn, WorkGraph as wr, IntrinsicFunctionResolver as wt, ATOMIC_FINAL_SNAPSHOT_TYPES as x, expectedOwnerParam as xi, malformedResourcesWarning as xn, shouldRetainResource as xr, deleteSkipReason as xt, mergeNoChangeOutputs as y, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as yi, malformedRenderedContainersWarning as yn, orphansAfterRollback as yr, UNSPECIFIED_SKIP_REASON as yt, isWarmThroughputDecrease as z, DynamicReferenceRegionAmbiguousError as zi, TemplateParser as zn, buildDenyExternalAccessPolicy as zr, assertRegionMatch as zt };
|
|
45705
|
-
//# sourceMappingURL=deploy-engine-
|
|
45984
|
+
//# sourceMappingURL=deploy-engine-CfOsVRQB.js.map
|