@go-to-k/cdkd 0.285.8 → 0.285.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{asg-provider-CvYAGRMV.js → asg-provider-hNkWDuAJ.js} +2 -2
- package/dist/{asg-provider-CvYAGRMV.js.map → asg-provider-hNkWDuAJ.js.map} +1 -1
- package/dist/cli.js +2 -2
- package/dist/{deploy-engine-D4QA_B1V.js → deploy-engine-BhBR2d3w.js} +567 -16
- package/dist/deploy-engine-BhBR2d3w.js.map +1 -0
- package/dist/index.d.ts +10 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/{program-CjJoJOIE.js → program-B9vnKc3N.js} +216 -16
- package/dist/{program-CjJoJOIE.js.map → program-B9vnKc3N.js.map} +1 -1
- package/dist/{version-DFdq83MF.js → version-BWsyP2sq.js} +2 -2
- package/dist/{version-DFdq83MF.js.map → version-BWsyP2sq.js.map} +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-D4QA_B1V.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { _ as withStackName, d as applyDefaultNameForFallback, f as generateResourceName, h as looksLikeCdkdGeneratedName, m as getCurrentStackName, n as getLogger, p as generateResourceNameWithFallback, r as isStdoutReservedForPayload, s as getLiveRenderer } from "./logger-Dw-3y48G.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-BWsyP2sq.js";
|
|
4
4
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5
5
|
import { randomUUID } from "node:crypto";
|
|
6
6
|
import { CreateBucketCommand, DeleteObjectCommand, DeleteObjectsCommand, GetBucketLocationCommand, GetObjectCommand, HeadBucketCommand, HeadObjectCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchKey, PutBucketEncryptionCommand, PutBucketPolicyCommand, PutObjectCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
|
|
@@ -9638,6 +9638,297 @@ function isRecordedSecretExpression(expression) {
|
|
|
9638
9638
|
function clearRecordedSecretExpressions() {
|
|
9639
9639
|
recordedSecretExpressions$1.clear();
|
|
9640
9640
|
}
|
|
9641
|
+
/**
|
|
9642
|
+
* The plaintexts a pass recorded with NO EXPRESSION behind them — the
|
|
9643
|
+
* MASK-ONLY needle class (issue
|
|
9644
|
+
* [#2274](https://github.com/go-to-k/cdkd/issues/2274)).
|
|
9645
|
+
*
|
|
9646
|
+
* WHAT IT IS FOR. A Lambda-backed custom resource's handler can declare its
|
|
9647
|
+
* response `Data` sensitive with the documented `NoEcho: true` envelope field.
|
|
9648
|
+
* That value is GENERATED by the handler, so cdkd never substituted it from
|
|
9649
|
+
* anything: there is no `{{resolve:...}}` expression to rewrite it back onto,
|
|
9650
|
+
* which is exactly why {@link RecordedSecretValues} — a plaintext -> EXPRESSION
|
|
9651
|
+
* map — cannot hold it on its own terms. The value still must not sit in
|
|
9652
|
+
* `state.json`, so what gets persisted in its place is {@link SECRET_MASK}.
|
|
9653
|
+
*
|
|
9654
|
+
* WHY THE SAME MAP RATHER THAN A SECOND BAG. Every persistence reader in this
|
|
9655
|
+
* module already walks a `RecordedSecretValues` — `scrubResourceRecord`'s three
|
|
9656
|
+
* fields, the rollback journal's ops, the outputs bag, `maskSecretsInText`'s
|
|
9657
|
+
* log / error / event sites. Threading a parallel bag to each of them would be
|
|
9658
|
+
* a wide change with one place per reader to forget. Recording the pair as
|
|
9659
|
+
* `plaintext -> SECRET_MASK` means the whole-value arm of
|
|
9660
|
+
* {@link redactSecretsForState} substitutes the mask with no code change at
|
|
9661
|
+
* all, and every reader is covered by construction.
|
|
9662
|
+
*
|
|
9663
|
+
* THE SENTINEL VALUE **IS** THE MARKER — there is no side table, and an earlier
|
|
9664
|
+
* revision's `WeakMap<RecordedSecretValues, Set<string>>` was removed after a
|
|
9665
|
+
* mutation probe showed the extra conjunct could not be fenced AND pointed the
|
|
9666
|
+
* wrong way. Nothing but {@link recordMaskOnlyValue} ever writes
|
|
9667
|
+
* {@link SECRET_MASK} as a map VALUE (every other writer stores a whole
|
|
9668
|
+
* `{{resolve:...}}` token, and {@link recordCrossStackExpression} refuses
|
|
9669
|
+
* anything else), so the side table could only ever disagree about an entry
|
|
9670
|
+
* some future writer valued `***` by hand — and for THAT entry, withholding the
|
|
9671
|
+
* substring arm is the SAFE answer, which is what the side table would have
|
|
9672
|
+
* denied. Scope is unaffected: the MAP is already per-pass, so a mask cannot
|
|
9673
|
+
* reach another resource's bag any more than an expression can.
|
|
9674
|
+
*
|
|
9675
|
+
* THE ONE PLACE THE CLASSES MUST DIFFER: the SUBSTRING arm. Substituting an
|
|
9676
|
+
* EXPRESSION for a match inside a longer leaf is lossless — the persisted leaf
|
|
9677
|
+
* still names a value every downstream reader can re-resolve. Substituting a
|
|
9678
|
+
* MASK is not: an inline `***` is indistinguishable from a literal `***` a user
|
|
9679
|
+
* wrote, so no consumer can recognise it, and `cdkd drift --revert` /
|
|
9680
|
+
* `resolveReplayProps` would push the corrupted string to AWS. A mask is only
|
|
9681
|
+
* safe where it is RECOGNISABLE, and that means whole-leaf. So a mask-only
|
|
9682
|
+
* plaintext is excluded from the persist path's needle regex and reaches only
|
|
9683
|
+
* the whole-value arm — the same "weaker class, narrower blast radius" shape
|
|
9684
|
+
* PR #2415 established for its `inferred` needles, which likewise take a leaf
|
|
9685
|
+
* whole or not at all.
|
|
9686
|
+
*
|
|
9687
|
+
* {@link maskSecretsInText} is deliberately NOT narrowed the same way: its
|
|
9688
|
+
* output is a log line, an error message or an event, which nothing reads back
|
|
9689
|
+
* as a value, so a partial mask there costs nothing and closes an embedded
|
|
9690
|
+
* disclosure.
|
|
9691
|
+
*/
|
|
9692
|
+
/**
|
|
9693
|
+
* Record `plaintext` as MASK-ONLY in `secrets` — persist {@link SECRET_MASK} in
|
|
9694
|
+
* its place, with no expression to substitute (issue #2274).
|
|
9695
|
+
*
|
|
9696
|
+
* An EXPRESSION already recorded for the same plaintext WINS and this is a
|
|
9697
|
+
* no-op: an expression is strictly better than a mask (it is re-resolvable, it
|
|
9698
|
+
* survives `drift --revert` and the rollback replay, and it reaches the
|
|
9699
|
+
* substring arm), so a mask must never demote one. The reverse direction needs
|
|
9700
|
+
* no code: the resolver writes an expression straight into the map, and
|
|
9701
|
+
* {@link isMaskOnlyPlaintext} re-checks the map value, so a plaintext that
|
|
9702
|
+
* later acquires a real expression stops being mask-only immediately.
|
|
9703
|
+
*
|
|
9704
|
+
* A plaintext shorter than {@link MIN_NEEDLE_LENGTH} is REFUSED, and this floor
|
|
9705
|
+
* is the one place the mask class needs a bound the EXPRESSION class does not
|
|
9706
|
+
* (issue #2274 review). An expression-bearing needle below the threshold is
|
|
9707
|
+
* still substituted on the WHOLE-VALUE arm, and that is safe because the pair
|
|
9708
|
+
* came from a POSITION cdkd resolved: the leaf it rewrites provably held that
|
|
9709
|
+
* reference. A mask-only needle has no position behind it — it is a bare
|
|
9710
|
+
* plaintext the handler happened to return — so the whole-value arm masks EVERY
|
|
9711
|
+
* leaf equal to it, anywhere in the record. A handler answering
|
|
9712
|
+
* `Data: { Count: "7" }` would otherwise mask any property whose whole value is
|
|
9713
|
+
* `"7"`, unrecoverably (there is no expression to re-resolve) and on every
|
|
9714
|
+
* later run (the mask then trips `refuseRedactedAttributeReads`,
|
|
9715
|
+
* `refuseMaskedReplayBaseline` and the export blocker). The floor is the same
|
|
9716
|
+
* constant the substring arm already applies, so the two arms of this module
|
|
9717
|
+
* now agree about what is too short to be a distinguishing value.
|
|
9718
|
+
*
|
|
9719
|
+
* THE BOUND IS THE MODULE'S, NOT ONE THIS CHANNEL INVENTED, and it is stated
|
|
9720
|
+
* rather than overstated: {@link MIN_NEEDLE_LENGTH} is 4, so a FOUR-character
|
|
9721
|
+
* member (`"true"`) still becomes a needle and a property whose whole value is
|
|
9722
|
+
* `"true"` is still masked. Raising the floor here alone would fork the two
|
|
9723
|
+
* arms' idea of a distinguishing value, which is the disagreement the shared
|
|
9724
|
+
* constant exists to prevent. The remedy for that shape is a handler contract
|
|
9725
|
+
* — do not declare a whole response `NoEcho` when its `Data` mixes a secret
|
|
9726
|
+
* with short non-secret members — and it is asserted in
|
|
9727
|
+
* `secret-redaction-mask-only.test.ts` so the bound is a recorded decision
|
|
9728
|
+
* rather than a surprise.
|
|
9729
|
+
*
|
|
9730
|
+
* The empty string is refused by the same bound, and would be refused anyway
|
|
9731
|
+
* for the reason the value pass refuses it: it is not a distinguishing value,
|
|
9732
|
+
* and recording it would mask every empty leaf.
|
|
9733
|
+
*/
|
|
9734
|
+
function recordMaskOnlyValue(secrets, plaintext) {
|
|
9735
|
+
if (plaintext.length < 4) return;
|
|
9736
|
+
const existing = secrets.get(plaintext);
|
|
9737
|
+
if (existing !== void 0 && existing !== "***") return;
|
|
9738
|
+
secrets.set(plaintext, "***");
|
|
9739
|
+
}
|
|
9740
|
+
/**
|
|
9741
|
+
* Is `plaintext` a MASK-ONLY entry of `secrets`?
|
|
9742
|
+
*
|
|
9743
|
+
* Read off the MAP, so a plaintext this module marked as mask-only and the
|
|
9744
|
+
* resolver later records WITH an expression stops being one immediately —
|
|
9745
|
+
* which matters, because that entry has earned the substring arm back.
|
|
9746
|
+
*/
|
|
9747
|
+
function isMaskOnlyPlaintext(secrets, plaintext) {
|
|
9748
|
+
return secrets.get(plaintext) === "***";
|
|
9749
|
+
}
|
|
9750
|
+
/**
|
|
9751
|
+
* Record every STRING LEAF of `value` as a MASK-ONLY needle in `secrets`.
|
|
9752
|
+
*
|
|
9753
|
+
* The bag-shaped twin of {@link recordMaskOnlyValue}, used where a whole
|
|
9754
|
+
* `Data` / attributes object is declared sensitive at once. Non-string leaves
|
|
9755
|
+
* are skipped deliberately: the redaction walk matches by string value, so
|
|
9756
|
+
* there is nothing to key a number or a boolean on, and both are far too
|
|
9757
|
+
* collision-prone to be useful needles even if there were.
|
|
9758
|
+
*
|
|
9759
|
+
* `excluded` is the set of plaintexts CDKD ITSELF SUPPLIED to the resource, and
|
|
9760
|
+
* passing it is what keeps a handler from masking cdkd's own inputs back at it
|
|
9761
|
+
* (issue #2274 review). A handler echoing its `event.ResourceProperties` into
|
|
9762
|
+
* `Data` — the shape the CDK `Provider` framework's samples encourage — makes
|
|
9763
|
+
* `Data.X` equal to the resource's own `ServiceToken`, and recording THAT as a
|
|
9764
|
+
* needle rewrites `properties.ServiceToken` to `***` in the very record
|
|
9765
|
+
* `CustomResourceProvider.delete` reads it back from, where `'***'` is a
|
|
9766
|
+
* truthy string that passes both of that method's guards. Such a value is not
|
|
9767
|
+
* handler-GENERATED at all — it is in the synthesized template already — so
|
|
9768
|
+
* excluding it costs no secrecy.
|
|
9769
|
+
*/
|
|
9770
|
+
function recordMaskOnlyValuesIn(value, secrets, excluded) {
|
|
9771
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9772
|
+
const walk = (node) => {
|
|
9773
|
+
if (typeof node === "string") {
|
|
9774
|
+
if (excluded?.has(node) === true) return;
|
|
9775
|
+
recordMaskOnlyValue(secrets, node);
|
|
9776
|
+
return;
|
|
9777
|
+
}
|
|
9778
|
+
if (node === null || typeof node !== "object") return;
|
|
9779
|
+
if (seen.has(node)) return;
|
|
9780
|
+
seen.add(node);
|
|
9781
|
+
if (Array.isArray(node)) {
|
|
9782
|
+
for (const item of node) walk(item);
|
|
9783
|
+
return;
|
|
9784
|
+
}
|
|
9785
|
+
for (const child of Object.values(node)) walk(child);
|
|
9786
|
+
};
|
|
9787
|
+
walk(value);
|
|
9788
|
+
}
|
|
9789
|
+
/**
|
|
9790
|
+
* Every WHOLE string leaf of `value`, as a set — the `excluded` argument
|
|
9791
|
+
* {@link recordMaskOnlyValuesIn} takes, built from the resource's own resolved
|
|
9792
|
+
* template properties.
|
|
9793
|
+
*
|
|
9794
|
+
* WHOLE leaves only, matching the arm the mask class is served on: a mask-only
|
|
9795
|
+
* needle never reaches the substring scan, so a plaintext that merely OCCURS
|
|
9796
|
+
* inside a property is not something this exclusion has to answer for.
|
|
9797
|
+
*/
|
|
9798
|
+
function wholeStringLeavesOf(value) {
|
|
9799
|
+
const leaves = /* @__PURE__ */ new Set();
|
|
9800
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9801
|
+
const walk = (node) => {
|
|
9802
|
+
if (typeof node === "string") {
|
|
9803
|
+
leaves.add(node);
|
|
9804
|
+
return;
|
|
9805
|
+
}
|
|
9806
|
+
if (node === null || typeof node !== "object") return;
|
|
9807
|
+
if (seen.has(node)) return;
|
|
9808
|
+
seen.add(node);
|
|
9809
|
+
if (Array.isArray(node)) {
|
|
9810
|
+
for (const item of node) walk(item);
|
|
9811
|
+
return;
|
|
9812
|
+
}
|
|
9813
|
+
for (const child of Object.values(node)) walk(child);
|
|
9814
|
+
};
|
|
9815
|
+
walk(value);
|
|
9816
|
+
return leaves;
|
|
9817
|
+
}
|
|
9818
|
+
/**
|
|
9819
|
+
* Does `value` carry {@link SECRET_MASK} as a WHOLE string leaf?
|
|
9820
|
+
*
|
|
9821
|
+
* The recognition test every consumer of a REDACTED baseline shares (issue
|
|
9822
|
+
* #2274). A mask-only redaction is whole-leaf precisely so it stays
|
|
9823
|
+
* recognisable, and this is what recognises it — in the resolver (a persisted
|
|
9824
|
+
* attribute cdkd can no longer serve), in `cdkd drift` (a baseline that must
|
|
9825
|
+
* not be pushed by `--revert` nor overwritten by `--accept`), and in the
|
|
9826
|
+
* rollback replay (a desired bag that must not reach a provider).
|
|
9827
|
+
*
|
|
9828
|
+
* Whole-leaf EQUALITY, never containment: an inline `***` inside a longer
|
|
9829
|
+
* string is either a user's own literal or text this module never wrote, and
|
|
9830
|
+
* treating it as a mask would refuse ordinary values. The corresponding limit —
|
|
9831
|
+
* a NoEcho value EMBEDDED in a larger leaf keeps its plaintext — is the same
|
|
9832
|
+
* one the mask-only channel note above states, and is tracked separately.
|
|
9833
|
+
*
|
|
9834
|
+
* UNBOUNDED in depth, guarded by {@link WalkedContainers} — see that type for
|
|
9835
|
+
* why a depth cap here was a hole rather than a safety measure.
|
|
9836
|
+
*/
|
|
9837
|
+
function carriesSecretMask(value) {
|
|
9838
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9839
|
+
const walk = (node) => {
|
|
9840
|
+
if (typeof node === "string") return node === "***";
|
|
9841
|
+
if (node === null || typeof node !== "object") return false;
|
|
9842
|
+
if (seen.has(node)) return false;
|
|
9843
|
+
seen.add(node);
|
|
9844
|
+
if (Array.isArray(node)) return node.some((item) => walk(item));
|
|
9845
|
+
return Object.values(node).some((child) => walk(child));
|
|
9846
|
+
};
|
|
9847
|
+
return walk(value);
|
|
9848
|
+
}
|
|
9849
|
+
/**
|
|
9850
|
+
* The plaintexts the PERSIST path may scan for as SUBSTRINGS — every recorded
|
|
9851
|
+
* one except the mask-only class. See the mask-only channel note above for why the
|
|
9852
|
+
* mask class is whole-leaf only.
|
|
9853
|
+
*/
|
|
9854
|
+
function substringNeedlesOf(secrets) {
|
|
9855
|
+
const needles = [];
|
|
9856
|
+
for (const plaintext of secrets.keys()) if (!isMaskOnlyPlaintext(secrets, plaintext)) needles.push(plaintext);
|
|
9857
|
+
return needles;
|
|
9858
|
+
}
|
|
9859
|
+
/**
|
|
9860
|
+
* The EXPRESSIONS a pass recorded — `secrets.values()` minus the mask-only
|
|
9861
|
+
* class, whose "expression" is the mask sentinel rather than a reference.
|
|
9862
|
+
*
|
|
9863
|
+
* Removing this filter is an EQUIVALENT MUTANT and no test can red on it —
|
|
9864
|
+
* stated rather than claimed pinned. {@link SECRET_MASK} is not a
|
|
9865
|
+
* dynamic-reference token, so neither `isKnownSecretExpression` nor a skeleton
|
|
9866
|
+
* pattern can ever accept it as a candidate. It is kept because a list
|
|
9867
|
+
* documented as "the expressions this pass recorded" must not silently contain
|
|
9868
|
+
* something that is not one: the day a candidate test stops requiring token
|
|
9869
|
+
* SHAPE, the sentinel would be live in it.
|
|
9870
|
+
*/
|
|
9871
|
+
function recordedExpressionsOf(secrets) {
|
|
9872
|
+
const expressions = /* @__PURE__ */ new Set();
|
|
9873
|
+
for (const [plaintext, expression] of secrets) if (!isMaskOnlyPlaintext(secrets, plaintext)) expressions.add(expression);
|
|
9874
|
+
return expressions;
|
|
9875
|
+
}
|
|
9876
|
+
/**
|
|
9877
|
+
* The IN-RUN recovery channel for a stack OUTPUT this process masked (issue
|
|
9878
|
+
* [#2274](https://github.com/go-to-k/cdkd/issues/2274)).
|
|
9879
|
+
*
|
|
9880
|
+
* WHY IT EXISTS. Masking a `NoEcho` custom resource's `Data` on the way into
|
|
9881
|
+
* `state.json` is right within one stack, where `Fn::GetAtt` reads the value out
|
|
9882
|
+
* of the IN-MEMORY record and gets the plaintext. It breaks the moment the value
|
|
9883
|
+
* crosses a STACK boundary, because every cross-stack route reads the producer's
|
|
9884
|
+
* PERSISTED `state.outputs`: a nested stack's `Outputs.<Key>` (via
|
|
9885
|
+
* `NestedStackProvider.readChildOutputsAsAttributes`), `Fn::ImportValue` (via
|
|
9886
|
+
* the exports index or a state scan) and `Fn::GetStackOutput` all land on the
|
|
9887
|
+
* mask. Without this the FIRST deploy of a parent whose child exports such a
|
|
9888
|
+
* value would refuse — a template that deployed before this feature — which is
|
|
9889
|
+
* a regression rather than a trade.
|
|
9890
|
+
*
|
|
9891
|
+
* WHAT IT IS. `stack + region + output key -> the plaintext that key held
|
|
9892
|
+
* before redaction`, written at the moment the producer's outputs are redacted
|
|
9893
|
+
* and read at the three cross-stack sites above. It answers only for a producer
|
|
9894
|
+
* THIS PROCESS deployed in THIS run, which is exactly the population that has a
|
|
9895
|
+
* plaintext to hand back: a separate `cdkd deploy` of the consumer has none, and
|
|
9896
|
+
* that case is refused rather than guessed at.
|
|
9897
|
+
*
|
|
9898
|
+
* WHY THE COORDINATE, and not a plaintext-keyed set. A bare-plaintext store was
|
|
9899
|
+
* the shape PR #2415 was forced to WITHDRAW (`provenPublicExpressions`,
|
|
9900
|
+
* residual #2425): keyed on a value alone, one stack's answer is served to
|
|
9901
|
+
* another stack's identically-spelled read. Here the key names the producer
|
|
9902
|
+
* stack, its region and the output — so a hit is served only to a resolution
|
|
9903
|
+
* that asked for that exact output of that exact stack, i.e. to precisely the
|
|
9904
|
+
* reader that would have received the plaintext before this feature existed.
|
|
9905
|
+
* Nothing is widened.
|
|
9906
|
+
*
|
|
9907
|
+
* A RECOVERED VALUE IS STILL SECRET, and every reader re-registers it as a
|
|
9908
|
+
* mask-only needle in its OWN bag before using it — the recovery hands back the
|
|
9909
|
+
* value for the WIRE, never for persistence.
|
|
9910
|
+
*/
|
|
9911
|
+
const recoverableMaskedOutputs = /* @__PURE__ */ new Map();
|
|
9912
|
+
function maskedOutputKey(stackName, region, outputKey) {
|
|
9913
|
+
return `${stackName}\u0000${region}\u0000${outputKey}`;
|
|
9914
|
+
}
|
|
9915
|
+
/**
|
|
9916
|
+
* Remember the plaintext an output held before {@link SECRET_MASK} replaced it.
|
|
9917
|
+
* See {@link recoverableMaskedOutputs}.
|
|
9918
|
+
*/
|
|
9919
|
+
function recordRecoverableMaskedOutput(stackName, region, outputKey, plaintext) {
|
|
9920
|
+
recoverableMaskedOutputs.set(maskedOutputKey(stackName, region, outputKey), plaintext);
|
|
9921
|
+
}
|
|
9922
|
+
/**
|
|
9923
|
+
* The plaintext this process masked out of `stackName`'s `outputKey`, or
|
|
9924
|
+
* `undefined` when this run did not produce that output.
|
|
9925
|
+
*
|
|
9926
|
+
* `undefined` is the honest answer for a producer deployed by an EARLIER run:
|
|
9927
|
+
* the value is gone and cdkd must refuse rather than write the mask to AWS.
|
|
9928
|
+
*/
|
|
9929
|
+
function recoverMaskedOutput(stackName, region, outputKey) {
|
|
9930
|
+
return recoverableMaskedOutputs.get(maskedOutputKey(stackName, region, outputKey));
|
|
9931
|
+
}
|
|
9641
9932
|
const crossStackAssociations = /* @__PURE__ */ new WeakMap();
|
|
9642
9933
|
/** Poison for a key seen against two different (expression, plaintext) pairs. */
|
|
9643
9934
|
const CONFLICTING_CROSS_STACK = Symbol("conflicting cross-stack association");
|
|
@@ -12101,7 +12392,7 @@ function refuseUncertifiedReadbackPositions(bag, source, secrets, learn, mark) {
|
|
|
12101
12392
|
function redactSecretsForState(bag, secrets, source, rules = TEMPLATE_DERIVED_RULES) {
|
|
12102
12393
|
if (secrets.size === 0 && source === void 0) return bag;
|
|
12103
12394
|
if (source !== void 0) {
|
|
12104
|
-
const positioned = redactByPath(bag, source, secrets, rules,
|
|
12395
|
+
const positioned = redactByPath(bag, source, secrets, rules, recordedExpressionsOf(secrets));
|
|
12105
12396
|
if (!isReadbackProjectedFromState(rules)) return positioned;
|
|
12106
12397
|
const refused = refuseUncertifiedReadbackPositions(positioned, source, secrets);
|
|
12107
12398
|
const derived = deriveReadbackNeedles(bag, source, secrets, rules);
|
|
@@ -12109,7 +12400,7 @@ function redactSecretsForState(bag, secrets, source, rules = TEMPLATE_DERIVED_RU
|
|
|
12109
12400
|
const marks = refuseUncertifiedReadbackPositions(positioned, source, secrets, void 0, true);
|
|
12110
12401
|
return preferPositionDecisions(redactSecretsForState(bag, derived.certain), refused, bag, marks, derived.inferred);
|
|
12111
12402
|
}
|
|
12112
|
-
const regex = buildNeedleRegex(secrets
|
|
12403
|
+
const regex = buildNeedleRegex(substringNeedlesOf(secrets));
|
|
12113
12404
|
const wholeValueExpr = (s) => s === "" ? void 0 : secrets.get(s);
|
|
12114
12405
|
/**
|
|
12115
12406
|
* The SUBSTRING arm for a leaf the resolver substituted INTO rather than
|
|
@@ -12248,6 +12539,15 @@ function scrubResourceRecord(record, secrets, sourceProperties, observedRules) {
|
|
|
12248
12539
|
* Used on log lines and error messages where a resolved secret could otherwise
|
|
12249
12540
|
* be echoed. Whole-value and embedded matches are both masked. Returns `text`
|
|
12250
12541
|
* unchanged when there is nothing to mask.
|
|
12542
|
+
*
|
|
12543
|
+
* The MASK-ONLY class (issue #2274) participates here FULLY — `secrets.keys()`,
|
|
12544
|
+
* not the persist path's narrowed `substringNeedlesOf` — and the asymmetry is
|
|
12545
|
+
* deliberate rather than an oversight. The persist path withholds the substring
|
|
12546
|
+
* arm from a mask because an inline `***` is a value nothing downstream can
|
|
12547
|
+
* recognise or re-resolve; this output is a log line, an error message or an
|
|
12548
|
+
* event, which no consumer reads back as a value, so a partial mask costs
|
|
12549
|
+
* nothing and closes an EMBEDDED disclosure that would otherwise print. See
|
|
12550
|
+
* the mask-only channel note above.
|
|
12251
12551
|
*/
|
|
12252
12552
|
function maskSecretsInText(text, secrets) {
|
|
12253
12553
|
if (secrets.size === 0) return text;
|
|
@@ -18384,11 +18684,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
18384
18684
|
if (resource.resourceType === "AWS::Route53::HostedZone" && attributeName === "NameServers" && typeof flatValue === "string") {
|
|
18385
18685
|
const nameServers = flatValue === "" ? [] : flatValue.split(",");
|
|
18386
18686
|
this.logger.debug(`Normalized legacy Fn::GetAtt attribute: ${logicalId}.${attributeName} -> ${stringifyAttributeForLog(attributeName, nameServers)}`);
|
|
18387
|
-
return nameServers;
|
|
18687
|
+
return this.noteAttributeSecrecy(logicalId, attributeName, nameServers, context);
|
|
18388
18688
|
}
|
|
18389
18689
|
this.logger.debug(`Resolved Fn::GetAtt from attributes: ${logicalId}.${attributeName} -> ${stringifyAttributeForLog(attributeName, flatValue)}`);
|
|
18390
18690
|
if (resource.resourceType === NESTED_STACK_RESOURCE_TYPE && 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 }));
|
|
18391
|
-
return flatValue;
|
|
18691
|
+
return this.noteAttributeSecrecy(logicalId, attributeName, flatValue, context);
|
|
18392
18692
|
}
|
|
18393
18693
|
if (attributeName.includes(".")) {
|
|
18394
18694
|
const parts = attributeName.split(".");
|
|
@@ -18400,7 +18700,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
18400
18700
|
}
|
|
18401
18701
|
if (cursor !== void 0) {
|
|
18402
18702
|
this.logger.debug(`Resolved Fn::GetAtt from nested attributes: ${logicalId}.${attributeName} -> ${stringifyAttributeForLog(attributeName, cursor)}`);
|
|
18403
|
-
return cursor;
|
|
18703
|
+
return this.noteAttributeSecrecy(logicalId, attributeName, cursor, context);
|
|
18404
18704
|
}
|
|
18405
18705
|
}
|
|
18406
18706
|
}
|
|
@@ -18413,6 +18713,59 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
18413
18713
|
return value;
|
|
18414
18714
|
}
|
|
18415
18715
|
/**
|
|
18716
|
+
* Note what SECRECY the attribute just read carries, then hand it back
|
|
18717
|
+
* UNCHANGED (issue [#2274](https://github.com/go-to-k/cdkd/issues/2274)).
|
|
18718
|
+
*
|
|
18719
|
+
* Two independent notes, in the two directions a `NoEcho` custom-resource
|
|
18720
|
+
* `Data` value travels, and they are on one function because both are read
|
|
18721
|
+
* off the same value at the same instant:
|
|
18722
|
+
*
|
|
18723
|
+
* 1. **This run's fresh value.** If a provider declared this resource's
|
|
18724
|
+
* attributes `NoEcho` earlier in THIS deploy, every string leaf becomes a
|
|
18725
|
+
* MASK-ONLY needle in the CONSUMER's own bag, so the plaintext this
|
|
18726
|
+
* resolution is about to substitute into the dependent's properties is
|
|
18727
|
+
* masked when that record is persisted. Registering into
|
|
18728
|
+
* `context.recordedSecretValues` — the consumer's bag — rather than the
|
|
18729
|
+
* producer's is what makes it work at all: `perResourceSecrets` is keyed by
|
|
18730
|
+
* LOGICAL ID, so a needle recorded under the custom resource's own id is
|
|
18731
|
+
* not in the bag the DEPENDENT's record is scrubbed with.
|
|
18732
|
+
* 2. **A previous run's masked value.** If what came back IS the mask, this
|
|
18733
|
+
* attribute was redacted into `state.json` by an earlier deploy and cdkd
|
|
18734
|
+
* cannot recover it (there is no durable `NoEcho` flag and no expression to
|
|
18735
|
+
* re-resolve — issue #2449). It is recorded as a redacted READ so the
|
|
18736
|
+
* deploy engine can refuse to push the literal `***` to AWS.
|
|
18737
|
+
*
|
|
18738
|
+
* IT RETURNS THE VALUE, and passing through rather than mutating in place is
|
|
18739
|
+
* the point: a new attribute-serving branch is written as `return
|
|
18740
|
+
* this.noteAttributeSecrecy(...)` by imitation of the two that have it.
|
|
18741
|
+
* `Fn::GetAtt` must keep delivering the REAL value (CloudFormation does,
|
|
18742
|
+
* measured), so this can never rewrite what it is handed.
|
|
18743
|
+
*
|
|
18744
|
+
* IT IS NOT A GUARANTEE, and an earlier revision claimed it was ("impossible
|
|
18745
|
+
* to add a third branch that silently skips the note"). Nothing in the type
|
|
18746
|
+
* system stops a branch returning a value it never passed through here, and
|
|
18747
|
+
* one already did: the Route 53 `NameServers` legacy-shape normalization,
|
|
18748
|
+
* which reads the SAME persisted `attributes` bag and shipped without the
|
|
18749
|
+
* note (it takes it now). The rule the note actually needs is about the
|
|
18750
|
+
* SOURCE of the value — every branch serving one out of a PERSISTED
|
|
18751
|
+
* `attributes` bag must call this — and that is not a shape a compiler can
|
|
18752
|
+
* enforce. `constructGuardedAttribute`'s return is deliberately outside it:
|
|
18753
|
+
* that value is fetched from AWS in this run, not read back from state, so
|
|
18754
|
+
* it can be neither a stale mask nor a value a provider declared `NoEcho`.
|
|
18755
|
+
*
|
|
18756
|
+
* A context supplying NEITHER field — the diff / no-op resolver, `cdkd
|
|
18757
|
+
* scrub`, `cdkd import` — pays two undefined checks and gets its value back.
|
|
18758
|
+
*/
|
|
18759
|
+
noteAttributeSecrecy(logicalId, attributeName, value, context) {
|
|
18760
|
+
const declared = context.noEchoAttributeResources?.get(logicalId);
|
|
18761
|
+
if ((declared === true || declared !== void 0 && declared.has(attributeName)) && context.recordedSecretValues) recordMaskOnlyValuesIn(value, context.recordedSecretValues);
|
|
18762
|
+
if (context.redactedAttributeReads !== void 0 && carriesSecretMask(value)) {
|
|
18763
|
+
const read = `${logicalId}.${attributeName}`;
|
|
18764
|
+
if (!context.redactedAttributeReads.includes(read)) context.redactedAttributeReads.push(read);
|
|
18765
|
+
}
|
|
18766
|
+
return value;
|
|
18767
|
+
}
|
|
18768
|
+
/**
|
|
18416
18769
|
* Refuse a pre-#1681 PLACEHOLDER ARN served from the cached attribute map
|
|
18417
18770
|
* (issue #1729) — the `Fn::GetAtt` half of the guard
|
|
18418
18771
|
* {@link cfnRefValueFromPhysicalId} applies to `Ref`.
|
|
@@ -19376,7 +19729,15 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
19376
19729
|
* the producer's rather than the caller's — which is exactly what issue #2057
|
|
19377
19730
|
* says the other two copies get wrong.
|
|
19378
19731
|
*/
|
|
19379
|
-
async reresolveCrossStackValue(value, producerRegion, context, origin, sourceKey) {
|
|
19732
|
+
async reresolveCrossStackValue(value, producerRegion, context, origin, sourceKey, producerOutput) {
|
|
19733
|
+
if (carriesSecretMask(value)) {
|
|
19734
|
+
const recovered = producerOutput === void 0 || producerOutput.crossAccount === true ? void 0 : recoverMaskedOutput(producerOutput.stackName, producerOutput.region, producerOutput.outputKey);
|
|
19735
|
+
if (recovered !== void 0) {
|
|
19736
|
+
if (context.recordedSecretValues) recordMaskOnlyValuesIn(recovered, context.recordedSecretValues);
|
|
19737
|
+
return recovered;
|
|
19738
|
+
}
|
|
19739
|
+
if (context.redactedAttributeReads !== void 0 && !context.redactedAttributeReads.includes(origin)) context.redactedAttributeReads.push(origin);
|
|
19740
|
+
}
|
|
19380
19741
|
if (!carriesDynamicReference(value)) return value;
|
|
19381
19742
|
const resolver = this.resolverForProducerRegion(producerRegion);
|
|
19382
19743
|
const pinnedContext = producerRegion ? withoutProducerRegions(context) : context;
|
|
@@ -19497,7 +19858,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
19497
19858
|
if (entry && (!context.stackName || entry.producerStack !== context.stackName)) {
|
|
19498
19859
|
this.recordImport(context, exportName, entry.producerStack, entry.producerRegion);
|
|
19499
19860
|
this.logger.info(`Resolved Fn::ImportValue: ${loggedExportName} (from index: ${entry.producerStack} / ${entry.producerRegion}; ${carriesDynamicReference(entry.value) ? "redacted dynamic reference" : "literal value"})`);
|
|
19500
|
-
return await this.reresolveCrossStackValue(entry.value, entry.producerRegion, context, `Fn::ImportValue '${exportName}' (producer ${entry.producerStack} / ${entry.producerRegion})`, sourceKey
|
|
19861
|
+
return await this.reresolveCrossStackValue(entry.value, entry.producerRegion, context, `Fn::ImportValue '${exportName}' (producer ${entry.producerStack} / ${entry.producerRegion})`, sourceKey, {
|
|
19862
|
+
stackName: entry.producerStack,
|
|
19863
|
+
region: entry.producerRegion,
|
|
19864
|
+
outputKey: exportName
|
|
19865
|
+
});
|
|
19501
19866
|
}
|
|
19502
19867
|
}
|
|
19503
19868
|
const allStacks = await context.stateBackend.listStacks();
|
|
@@ -19544,7 +19909,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
19544
19909
|
continue;
|
|
19545
19910
|
}
|
|
19546
19911
|
}
|
|
19547
|
-
if (found) return await this.reresolveCrossStackValue(found.value, found.lookupRegion, context, `Fn::ImportValue '${exportName}' (producer ${found.refStack} / ${found.lookupRegion})`, sourceKey
|
|
19912
|
+
if (found) return await this.reresolveCrossStackValue(found.value, found.lookupRegion, context, `Fn::ImportValue '${exportName}' (producer ${found.refStack} / ${found.lookupRegion})`, sourceKey, {
|
|
19913
|
+
stackName: found.refStack,
|
|
19914
|
+
region: found.lookupRegion,
|
|
19915
|
+
outputKey: exportName
|
|
19916
|
+
});
|
|
19548
19917
|
if (this.cfnFallback) {
|
|
19549
19918
|
const cfnExport = await this.lookupCfnExport(exportName, context);
|
|
19550
19919
|
if (cfnExport) {
|
|
@@ -19797,7 +20166,12 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
19797
20166
|
this.logger.info(`Resolved Fn::GetStackOutput: StackName=${loggedStackName}, Region=${region}, OutputName=${loggedOutputName}${roleArn ? `, RoleArn=${roleArn}` : ""} (${carriesDynamicReference(value) ? "redacted dynamic reference" : "literal value"})`);
|
|
19798
20167
|
if (!roleArn) this.recordOutputRead(context, stackName, region, outputName);
|
|
19799
20168
|
if (roleArn && !context.skipDynamicReferences && carriesDynamicReference(value)) throw markNonRetryable(new CrossAccountSecretRefusalError(`Fn::GetStackOutput: output '${outputName}' of stack '${stackName}' (${region}) is a redacted dynamic reference, and this is a CROSS-ACCOUNT reference (RoleArn ${roleArn}). cdkd will not resolve a producer account's secret with the consumer's credentials — a same-named secret in the consumer account would answer instead. Export a non-secret value (e.g. the secret's ARN) and resolve it in the consumer stack, or reference the producer stack from within its own account.`));
|
|
19800
|
-
return await this.reresolveCrossStackValue(value, region, context, `Fn::GetStackOutput '${outputName}' (producer ${stackName} / ${region})`, sourceKey
|
|
20169
|
+
return await this.reresolveCrossStackValue(value, region, context, `Fn::GetStackOutput '${outputName}' (producer ${stackName} / ${region})`, sourceKey, {
|
|
20170
|
+
stackName,
|
|
20171
|
+
region,
|
|
20172
|
+
outputKey: outputName,
|
|
20173
|
+
...roleArn ? { crossAccount: true } : {}
|
|
20174
|
+
});
|
|
19801
20175
|
}
|
|
19802
20176
|
/**
|
|
19803
20177
|
* Push a resolved `Fn::GetStackOutput` into the consumer's
|
|
@@ -21462,7 +21836,7 @@ var CloudControlProvider = class {
|
|
|
21462
21836
|
const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
|
|
21463
21837
|
if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
|
|
21464
21838
|
this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
|
|
21465
|
-
const { ASGProvider } = await import("./asg-provider-
|
|
21839
|
+
const { ASGProvider } = await import("./asg-provider-hNkWDuAJ.js").then((n) => n.n);
|
|
21466
21840
|
return withIndeterminateGuard(await new ASGProvider().delete(logicalId, physicalId, resourceType, _properties, context), indeterminateGuard);
|
|
21467
21841
|
}
|
|
21468
21842
|
const isProtectedEc2Instance = context?.removeProtection === true && resourceType === "AWS::EC2::Instance";
|
|
@@ -23738,7 +24112,8 @@ var CustomResourceProvider = class CustomResourceProvider {
|
|
|
23738
24112
|
this.logger.debug(`Successfully created custom resource ${logicalId}: ${physicalId}`);
|
|
23739
24113
|
return {
|
|
23740
24114
|
physicalId,
|
|
23741
|
-
attributes
|
|
24115
|
+
attributes,
|
|
24116
|
+
...cfnResponse.NoEcho === true && { noEchoAttributes: true }
|
|
23742
24117
|
};
|
|
23743
24118
|
} catch (error) {
|
|
23744
24119
|
const cause = error instanceof Error ? error : void 0;
|
|
@@ -23773,7 +24148,8 @@ var CustomResourceProvider = class CustomResourceProvider {
|
|
|
23773
24148
|
return {
|
|
23774
24149
|
physicalId: newPhysicalId,
|
|
23775
24150
|
wasReplaced,
|
|
23776
|
-
attributes
|
|
24151
|
+
attributes,
|
|
24152
|
+
...cfnResponse.NoEcho === true && { noEchoAttributes: true }
|
|
23777
24153
|
};
|
|
23778
24154
|
} catch (error) {
|
|
23779
24155
|
const cause = error instanceof Error ? error : void 0;
|
|
@@ -24285,6 +24661,7 @@ var CustomResourceProvider = class CustomResourceProvider {
|
|
|
24285
24661
|
const result = { Status: "SUCCESS" };
|
|
24286
24662
|
if (payload.PhysicalResourceId) result.PhysicalResourceId = payload.PhysicalResourceId;
|
|
24287
24663
|
if (payload.Data) result.Data = payload.Data;
|
|
24664
|
+
if (payload.NoEcho === true) result.NoEcho = true;
|
|
24288
24665
|
return result;
|
|
24289
24666
|
}
|
|
24290
24667
|
hasDirectPayload = Object.keys(payload).length > 0;
|
|
@@ -29882,6 +30259,37 @@ async function resolveReplayProps(props, resolvers, secrets, execCtx, logicalId)
|
|
|
29882
30259
|
return await walk(props, "");
|
|
29883
30260
|
}
|
|
29884
30261
|
/**
|
|
30262
|
+
* Refuse to REPLAY a bag whose recorded baseline holds a REDACTION MASK (issue
|
|
30263
|
+
* [#2274](https://github.com/go-to-k/cdkd/issues/2274)).
|
|
30264
|
+
*
|
|
30265
|
+
* The rollback twin of `drift --revert`'s
|
|
30266
|
+
* `preserveLiveValuesAtMaskedLeaves`, and it exists for the same reason: a
|
|
30267
|
+
* `NoEcho` custom resource's `Data` resolved into a dependent's property is
|
|
30268
|
+
* persisted as {@link SECRET_MASK}, because there is no expression to store in
|
|
30269
|
+
* its place — and this executor replays a persisted bag straight to
|
|
30270
|
+
* `provider.update()` / `create()`. Without a guard the literal `***` would be
|
|
30271
|
+
* written onto the live resource, which is the issue #1498 / #1501
|
|
30272
|
+
* data-corruption class.
|
|
30273
|
+
*
|
|
30274
|
+
* IT REFUSES rather than substituting, and that is the difference from the
|
|
30275
|
+
* drift twin. `--revert` holds an AWS-current readback beside the baseline, so
|
|
30276
|
+
* it can leave the position exactly as AWS has it; a replay holds no readback
|
|
30277
|
+
* at all — `previousState.properties` IS its only source — so there is nothing
|
|
30278
|
+
* to fall back to. Failing the ONE op with an actionable message is strictly
|
|
30279
|
+
* better than writing a value cdkd knows is wrong, and the per-op failure
|
|
30280
|
+
* accounting this file already has is what carries it.
|
|
30281
|
+
*
|
|
30282
|
+
* CALLED ON THE WRITTEN SIDE ONLY. Each revert arm resolves two bags; the other
|
|
30283
|
+
* one becomes the provider's `previousProperties`, where a mask is harmless (a
|
|
30284
|
+
* patch provider comparing `***` against the desired value simply sees a
|
|
30285
|
+
* change, which is the correct conclusion — the live value is not what state
|
|
30286
|
+
* records). Refusing there would block rollbacks that have no problem.
|
|
30287
|
+
*/
|
|
30288
|
+
function refuseMaskedReplayBaseline(props, logicalId) {
|
|
30289
|
+
if (props === void 0 || !carriesSecretMask(props)) return;
|
|
30290
|
+
throw new CdkdError(`Cannot roll ${logicalId} back: its recorded baseline holds the redaction mask ('${"***"}') where a NoEcho custom-resource value was resolved, so cdkd would write that literal to the live resource. Restore the property with 'cdkd deploy' AFTER forcing that custom resource to update (change one of its properties, e.g. a nonce), so its handler runs again and supplies the real value — an ordinary re-deploy leaves the resource unchanged, so the handler does not run and the mask stays. See https://github.com/go-to-k/cdkd/issues/2449.`, "ROLLBACK_REDACTED_BASELINE");
|
|
30291
|
+
}
|
|
30292
|
+
/**
|
|
29885
30293
|
* The replay's resolvers: the stack's own, plus one pinned sibling per FOREIGN
|
|
29886
30294
|
* region an ARN-named reference asks for (issue #2057).
|
|
29887
30295
|
*
|
|
@@ -30336,6 +30744,7 @@ async function replaySingle(op, stateResources, stackName, ctx, resolver, orphan
|
|
|
30336
30744
|
const current = stateResources[op.logicalId];
|
|
30337
30745
|
const prev = op.previousState;
|
|
30338
30746
|
const resolvedPrevProps = await resolveReplayProps(prev.properties, resolver, secrets, ctx, op.logicalId) ?? {};
|
|
30747
|
+
refuseMaskedReplayBaseline(resolvedPrevProps, op.logicalId);
|
|
30339
30748
|
recordNestedStackParameterExpressions(secrets, op.resourceType, resolvedPrevProps, prev.properties, STATE_DERIVED_RULES);
|
|
30340
30749
|
logger.info(` Rollback: Reversing replacement of ${op.logicalId} (${op.resourceType}) — re-creating the old resource and deleting the new one`);
|
|
30341
30750
|
if (STATEFUL_TYPES.has(op.resourceType)) logger.warn(` ⚠ ${op.logicalId} (${op.resourceType}) is a stateful type — the old physical resource's data was destroyed by the replacement and CANNOT be recovered; the re-created resource starts empty.`);
|
|
@@ -30429,6 +30838,7 @@ async function replaySingle(op, stateResources, stackName, ctx, resolver, orphan
|
|
|
30429
30838
|
provisionedBy: op.provisionedBy
|
|
30430
30839
|
});
|
|
30431
30840
|
const desiredProps = await resolveReplayProps(previousState.properties, resolver, secrets, ctx, op.logicalId);
|
|
30841
|
+
refuseMaskedReplayBaseline(desiredProps, op.logicalId);
|
|
30432
30842
|
const currentProps = await resolveReplayProps(current.properties, resolver, secrets, ctx, op.logicalId);
|
|
30433
30843
|
recordNestedStackParameterExpressions(secrets, op.resourceType, desiredProps, previousState.properties, STATE_DERIVED_RULES);
|
|
30434
30844
|
const revertResult = await updateWithRollbackRetry(provider, [
|
|
@@ -30585,6 +30995,7 @@ async function replayFailedOperations(failedOps, stateResources, stackName, ctx,
|
|
|
30585
30995
|
provisionedBy: op.provisionedBy ?? current.provisionedBy
|
|
30586
30996
|
});
|
|
30587
30997
|
const desiredProps = await resolveReplayProps(prev.properties, resolver, secrets, ctx, op.logicalId);
|
|
30998
|
+
refuseMaskedReplayBaseline(desiredProps, op.logicalId);
|
|
30588
30999
|
const attemptedProps = await resolveReplayProps(op.attemptedProperties ?? current.properties, resolver, secrets, ctx, op.logicalId);
|
|
30589
31000
|
recordNestedStackParameterExpressions(secrets, op.resourceType, desiredProps, prev.properties, STATE_DERIVED_RULES);
|
|
30590
31001
|
const revertFailedResult = await updateWithRollbackRetry(provider, [
|
|
@@ -31431,6 +31842,29 @@ var DeployEngine = class {
|
|
|
31431
31842
|
*/
|
|
31432
31843
|
perResourceSecrets = /* @__PURE__ */ new Map();
|
|
31433
31844
|
/**
|
|
31845
|
+
* Logical ids whose provider declared THIS RUN's `attributes` sensitive
|
|
31846
|
+
* (`ResourceCreateResult.noEchoAttributes` — issue
|
|
31847
|
+
* [#2274](https://github.com/go-to-k/cdkd/issues/2274)). One producer today:
|
|
31848
|
+
* `CustomResourceProvider`, relaying the handler's `NoEcho: true`.
|
|
31849
|
+
*
|
|
31850
|
+
* IN-RUN ONLY, and that is the whole shape of the feature rather than a
|
|
31851
|
+
* shortcut. `NoEcho` arrives on a RESPONSE, so cdkd knows it exactly when the
|
|
31852
|
+
* handler answered — this deploy — and `ResourceState` carries no durable
|
|
31853
|
+
* per-attribute flag to remember it by (a v9 -> v10 schema bump, issue
|
|
31854
|
+
* [#2449](https://github.com/go-to-k/cdkd/issues/2449)). Within the run that
|
|
31855
|
+
* is enough: the DAG provisions the custom resource before anything that
|
|
31856
|
+
* depends on it, so every dependent's resolution sees the entry. Across runs
|
|
31857
|
+
* the persisted `***` is the signal instead — see
|
|
31858
|
+
* `ResolverContext.redactedAttributeReads`.
|
|
31859
|
+
*
|
|
31860
|
+
* Reset per `deploy()`, like `perResourceSecrets`.
|
|
31861
|
+
*
|
|
31862
|
+
* `true` means the WHOLE attributes bag is sensitive (a custom resource's
|
|
31863
|
+
* `NoEcho` response); a SET names the sensitive members only (a nested
|
|
31864
|
+
* stack's `Outputs.<Key>` entries — see `NoEchoAttributesResult`).
|
|
31865
|
+
*/
|
|
31866
|
+
noEchoAttributeResources = /* @__PURE__ */ new Map();
|
|
31867
|
+
/**
|
|
31434
31868
|
* PER-RESOURCE unresolved TEMPLATE properties, keyed by logicalId (issues
|
|
31435
31869
|
* #1904 / #1900). The redaction choke point uses this as the POSITION source:
|
|
31436
31870
|
* wherever the template leaf is a `{{resolve:...}}` string, state persists
|
|
@@ -31518,6 +31952,7 @@ var DeployEngine = class {
|
|
|
31518
31952
|
this.recordedImports = [];
|
|
31519
31953
|
this.recordedOutputReads = [];
|
|
31520
31954
|
this.perResourceSecrets = /* @__PURE__ */ new Map();
|
|
31955
|
+
this.noEchoAttributeResources = /* @__PURE__ */ new Map();
|
|
31521
31956
|
this.perResourceTemplateProps = /* @__PURE__ */ new Map();
|
|
31522
31957
|
this.outputSecrets = /* @__PURE__ */ new Map();
|
|
31523
31958
|
this.outputsTemplateSource = {};
|
|
@@ -31545,7 +31980,9 @@ var DeployEngine = class {
|
|
|
31545
31980
|
recordedImports: this.recordedImports,
|
|
31546
31981
|
recordedOutputReads: this.recordedOutputReads,
|
|
31547
31982
|
...this.options.inheritedSecrets && this.options.inheritedSecrets.size > 0 && { inheritedSecrets: this.options.inheritedSecrets },
|
|
31548
|
-
recordedSecretValues
|
|
31983
|
+
recordedSecretValues,
|
|
31984
|
+
noEchoAttributeResources: this.noEchoAttributeResources,
|
|
31985
|
+
redactedAttributeReads: []
|
|
31549
31986
|
};
|
|
31550
31987
|
}
|
|
31551
31988
|
/**
|
|
@@ -31594,6 +32031,30 @@ var DeployEngine = class {
|
|
|
31594
32031
|
* resolving one secret collapsed onto whichever expression was recorded last
|
|
31595
32032
|
* at all three.
|
|
31596
32033
|
*/
|
|
32034
|
+
/**
|
|
32035
|
+
* Record the plaintext behind every output {@link redactOutputs} just masked,
|
|
32036
|
+
* for the duration of THIS PROCESS (issue #2274).
|
|
32037
|
+
*
|
|
32038
|
+
* Per KEY, comparing the two bags rather than re-deriving from the secrets
|
|
32039
|
+
* map: what matters is whether the persisted value at this key IS a mask that
|
|
32040
|
+
* the resolved value was not, which is exactly "this key's plaintext is about
|
|
32041
|
+
* to become unreadable". A key already carrying `***` before redaction — a
|
|
32042
|
+
* value read back out of a previous run's state — is skipped, because there
|
|
32043
|
+
* is no plaintext behind it to remember.
|
|
32044
|
+
*
|
|
32045
|
+
* Called only from the REAL-DEPLOY outputs pass. The other two `redactOutputs`
|
|
32046
|
+
* callers hand it a bag from a previous generation, where a mask is already
|
|
32047
|
+
* unrecoverable and pretending otherwise would serve a stale value.
|
|
32048
|
+
*/
|
|
32049
|
+
rememberRecoverableMaskedOutputs(stackName, resolved, redacted) {
|
|
32050
|
+
if (resolved === redacted) return;
|
|
32051
|
+
for (const [key, redactedValue] of Object.entries(redacted)) {
|
|
32052
|
+
if (!carriesSecretMask(redactedValue)) continue;
|
|
32053
|
+
const plaintext = resolved[key];
|
|
32054
|
+
if (plaintext === void 0 || carriesSecretMask(plaintext)) continue;
|
|
32055
|
+
recordRecoverableMaskedOutput(stackName, this.stackRegion, key, plaintext);
|
|
32056
|
+
}
|
|
32057
|
+
}
|
|
31597
32058
|
redactOutputs(outputs) {
|
|
31598
32059
|
if (this.outputSecrets.size === 0) return outputs;
|
|
31599
32060
|
return redactSecretsForState(outputs, this.outputSecrets, this.outputsSourceUsable ? this.outputsTemplateSource : void 0, TEMPLATE_SOURCED_RULES);
|
|
@@ -31625,6 +32086,85 @@ var DeployEngine = class {
|
|
|
31625
32086
|
* same shape — so it is redacted against ITSELF via `scrubResourceRecord`,
|
|
31626
32087
|
* the same #1900 fallback an UNCHANGED resource takes.
|
|
31627
32088
|
*/
|
|
32089
|
+
/**
|
|
32090
|
+
* Take a provider's `noEchoAttributes` declaration and turn it into REDACTION
|
|
32091
|
+
* (issue [#2274](https://github.com/go-to-k/cdkd/issues/2274)).
|
|
32092
|
+
*
|
|
32093
|
+
* TWO registrations, and both are needed because `perResourceSecrets` is
|
|
32094
|
+
* keyed by LOGICAL ID:
|
|
32095
|
+
*
|
|
32096
|
+
* - the values go into the PRODUCER's own bag, which is what
|
|
32097
|
+
* `scrubResourceRecord` redacts this record's `attributes` with;
|
|
32098
|
+
* - the logical id goes into {@link noEchoAttributeResources}, which every
|
|
32099
|
+
* later resolution consults, so a DEPENDENT that resolves an `Fn::GetAtt`
|
|
32100
|
+
* here records the same plaintext into ITS bag and its resolved
|
|
32101
|
+
* `properties` are masked too. Without the second half the custom resource's
|
|
32102
|
+
* record would be clean while the SSM parameter that consumed it still held
|
|
32103
|
+
* the plaintext — a line that cannot be explained to someone who set
|
|
32104
|
+
* `NoEcho` expecting "not in state".
|
|
32105
|
+
*
|
|
32106
|
+
* Called AFTER the provider returns and BEFORE the state record is built, so
|
|
32107
|
+
* the needles exist by the time anything is persisted. Nothing is masked in
|
|
32108
|
+
* memory: `stateResources[logicalId].attributes` keeps the REAL value, which
|
|
32109
|
+
* is what the resolver serves to dependents in this same run.
|
|
32110
|
+
*
|
|
32111
|
+
* `ownProperties` is the resource's OWN resolved template bag, and passing it
|
|
32112
|
+
* is what stops a handler from masking cdkd's inputs back at it (issue #2274
|
|
32113
|
+
* review). Its whole string leaves are EXCLUDED from the needles: a handler
|
|
32114
|
+
* echoing `event.ResourceProperties` into its `Data` — the shape the CDK
|
|
32115
|
+
* `Provider` samples encourage — makes `Data.X` equal the resource's own
|
|
32116
|
+
* `ServiceToken`, and registering that rewrites `properties.ServiceToken` to
|
|
32117
|
+
* `***` in the record `CustomResourceProvider.delete` reads it back from,
|
|
32118
|
+
* where the mask is a truthy string that passes both of that method's guards.
|
|
32119
|
+
* A value already present in the template is not handler-GENERATED, so
|
|
32120
|
+
* excluding it gives up no secrecy — and where the template value IS a
|
|
32121
|
+
* resolved secret it already carries a real EXPRESSION needle, which
|
|
32122
|
+
* `recordMaskOnlyValue` would refuse to demote anyway.
|
|
32123
|
+
*/
|
|
32124
|
+
registerNoEchoAttributes(logicalId, result, secrets, ownProperties) {
|
|
32125
|
+
const attributes = result.attributes;
|
|
32126
|
+
if (attributes === void 0) return;
|
|
32127
|
+
const excluded = ownProperties === void 0 ? void 0 : wholeStringLeavesOf(ownProperties);
|
|
32128
|
+
if (result.noEchoAttributes === true) {
|
|
32129
|
+
this.noEchoAttributeResources.set(logicalId, true);
|
|
32130
|
+
recordMaskOnlyValuesIn(attributes, secrets, excluded);
|
|
32131
|
+
return;
|
|
32132
|
+
}
|
|
32133
|
+
const names = (result.noEchoAttributeNames ?? []).filter((name) => name in attributes);
|
|
32134
|
+
if (names.length === 0) return;
|
|
32135
|
+
this.noEchoAttributeResources.set(logicalId, new Set(names));
|
|
32136
|
+
for (const name of names) recordMaskOnlyValuesIn(attributes[name], secrets, excluded);
|
|
32137
|
+
}
|
|
32138
|
+
/**
|
|
32139
|
+
* Refuse to provision a resource whose resolution served a REDACTED attribute
|
|
32140
|
+
* out of a previous deploy's state (issue #2274).
|
|
32141
|
+
*
|
|
32142
|
+
* The unavoidable cost of masking a `NoEcho` custom resource's `Data`: state
|
|
32143
|
+
* then holds `***`, and cdkd cannot get the value back without re-invoking
|
|
32144
|
+
* the handler, which is a SIDE-EFFECTING operation it must not perform just
|
|
32145
|
+
* to fill in a property. Since `ResourceState` carries no durable `NoEcho`
|
|
32146
|
+
* flag (issue #2449), there is not even a way to tell the user which
|
|
32147
|
+
* attribute it was without this record.
|
|
32148
|
+
*
|
|
32149
|
+
* REFUSING IS THE SAFE DIRECTION and the alternative is not "it works": the
|
|
32150
|
+
* literal `***` would be written to the live resource by any provider that
|
|
32151
|
+
* sends its desired bag wholesale (`PutParameter` and every
|
|
32152
|
+
* `Put*Configuration`), which is the issue #1498 / #1501 data-corruption
|
|
32153
|
+
* class. A loud failure naming the remedy is strictly better than a silent
|
|
32154
|
+
* wrong write.
|
|
32155
|
+
*
|
|
32156
|
+
* NARROW BY CONSTRUCTION. The bag is only non-empty when a `Fn::GetAtt`
|
|
32157
|
+
* actually served a masked attribute during THIS resource's resolution, so a
|
|
32158
|
+
* resource whose properties merely happen to contain the string `***` is
|
|
32159
|
+
* untouched — which is why the check is not "does `resolvedProps` hold the
|
|
32160
|
+
* mask". And the diff pass does not consult the bag at all, so an untouched
|
|
32161
|
+
* stack still reports NO_CHANGE and deploys.
|
|
32162
|
+
*/
|
|
32163
|
+
refuseRedactedAttributeReads(logicalId, resourceType, context) {
|
|
32164
|
+
const reads = context.redactedAttributeReads;
|
|
32165
|
+
if (reads === void 0 || reads.length === 0) return;
|
|
32166
|
+
throw new ProvisioningError(`Cannot resolve ${reads.join(", ")} for ${logicalId}: cdkd's recorded state holds only the redaction mask there, and the value is not recoverable from state. That happens when a custom resource handler declared its response NoEcho: true — the value is generated by the handler, so cdkd has nothing to re-derive it from and must not write the literal mask to AWS. Two remedies: force that custom resource to update (change one of its properties, e.g. a nonce / version property) so its handler runs again and supplies the value in this same run; or stop setting NoEcho on that response. If the value comes from ANOTHER stack, the producer and this stack must deploy in ONE run (cdkd deploy --all) with the producer's custom resource actually running — re-deploying the producer by itself does not help, because it re-masks the value on the way into its own state. See https://github.com/go-to-k/cdkd/issues/2449.`, resourceType, logicalId);
|
|
32167
|
+
}
|
|
31628
32168
|
redactOperationsForJournal(operations) {
|
|
31629
32169
|
return operations.map((op) => {
|
|
31630
32170
|
const secrets = this.perResourceSecrets.get(op.logicalId);
|
|
@@ -32272,7 +32812,9 @@ var DeployEngine = class {
|
|
|
32272
32812
|
let outputs;
|
|
32273
32813
|
try {
|
|
32274
32814
|
outputs = await this.resolveOutputs(template, newResources, stackName, parameterValues, conditions);
|
|
32815
|
+
const resolvedOutputsBeforeRedaction = outputs;
|
|
32275
32816
|
outputs = this.redactOutputs(outputs);
|
|
32817
|
+
this.rememberRecoverableMaskedOutputs(stackName, resolvedOutputsBeforeRedaction, outputs);
|
|
32276
32818
|
} catch (outputError) {
|
|
32277
32819
|
await this.persistStateAfterOutputFailure(stackName, currentState, newResources, currentEtag, pendingMigration);
|
|
32278
32820
|
await this.writeRollbackJournalSegment(stackName, completedOperations, failedOperations, "no-rollback-failure", currentEtag === void 0);
|
|
@@ -32688,6 +33230,7 @@ var DeployEngine = class {
|
|
|
32688
33230
|
}, stackName);
|
|
32689
33231
|
if (context.recordedSecretValues) this.perResourceSecrets.set(logicalId, context.recordedSecretValues);
|
|
32690
33232
|
const resolvedProps = await this.resolver.resolve(desiredProps, context);
|
|
33233
|
+
this.refuseRedactedAttributeReads(logicalId, resourceType, context);
|
|
32691
33234
|
this.perResourceTemplateProps.set(logicalId, desiredProps);
|
|
32692
33235
|
const createSecrets = context.recordedSecretValues ?? /* @__PURE__ */ new Map();
|
|
32693
33236
|
recordNestedStackParameterExpressions(createSecrets, resourceType, resolvedProps, desiredProps);
|
|
@@ -32700,6 +33243,7 @@ var DeployEngine = class {
|
|
|
32700
33243
|
const createProvider = createDecision.provider;
|
|
32701
33244
|
const createProps = createDecision.provisionedBy === "cc-api" ? this.preparePropertiesForCcApi(resourceType, resolvedProps, logicalId) : resolvedProps;
|
|
32702
33245
|
const result = await this.withRetry(() => withCurrentResourceSecrets(createSecrets, () => createProvider.create(logicalId, resourceType, createProps, { maskSecrets: createSecretMasker(createSecrets) })), logicalId, void 0, void 0, createProvider);
|
|
33246
|
+
this.registerNoEchoAttributes(logicalId, result, createSecrets, resolvedProps);
|
|
32703
33247
|
const dependencies = this.extractAllDependencies(template, logicalId);
|
|
32704
33248
|
const templateAttrs = this.extractTemplateAttributes(template, logicalId);
|
|
32705
33249
|
stateResources[logicalId] = {
|
|
@@ -32734,6 +33278,7 @@ var DeployEngine = class {
|
|
|
32734
33278
|
const updateSecrets = context.recordedSecretValues ?? /* @__PURE__ */ new Map();
|
|
32735
33279
|
this.perResourceSecrets.set(logicalId, updateSecrets);
|
|
32736
33280
|
const resolvedProps = await this.resolver.resolve(desiredProps, context);
|
|
33281
|
+
this.refuseRedactedAttributeReads(logicalId, resourceType, context);
|
|
32737
33282
|
this.perResourceTemplateProps.set(logicalId, desiredProps);
|
|
32738
33283
|
recordNestedStackParameterExpressions(updateSecrets, resourceType, resolvedProps, desiredProps);
|
|
32739
33284
|
this.auditResolvedAssetReferences(logicalId, resourceType, resolvedProps);
|
|
@@ -32875,6 +33420,7 @@ var DeployEngine = class {
|
|
|
32875
33420
|
}
|
|
32876
33421
|
}
|
|
32877
33422
|
}
|
|
33423
|
+
this.registerNoEchoAttributes(logicalId, createResult, updateSecrets, resolvedProps);
|
|
32878
33424
|
stateResources[logicalId] = {
|
|
32879
33425
|
physicalId: createResult.physicalId,
|
|
32880
33426
|
resourceType,
|
|
@@ -32950,6 +33496,11 @@ var DeployEngine = class {
|
|
|
32950
33496
|
}
|
|
32951
33497
|
if (result.wasReplaced) this.logger.info(`Resource ${logicalId} was replaced: ${currentResource.physicalId} -> ${result.physicalId}`);
|
|
32952
33498
|
const carriedAttributes = result.attributes ?? (result.wasReplaced ? void 0 : currentResource.attributes);
|
|
33499
|
+
this.registerNoEchoAttributes(logicalId, {
|
|
33500
|
+
...carriedAttributes && { attributes: carriedAttributes },
|
|
33501
|
+
...result.noEchoAttributes === true && { noEchoAttributes: true },
|
|
33502
|
+
...result.noEchoAttributeNames && { noEchoAttributeNames: result.noEchoAttributeNames }
|
|
33503
|
+
}, updateSecrets, resolvedProps);
|
|
32953
33504
|
stateResources[logicalId] = {
|
|
32954
33505
|
physicalId: result.physicalId,
|
|
32955
33506
|
resourceType,
|
|
@@ -33406,5 +33957,5 @@ var DeployEngine = class {
|
|
|
33406
33957
|
};
|
|
33407
33958
|
|
|
33408
33959
|
//#endregion
|
|
33409
|
-
export { DEFAULT_STATE_PREFIX as $,
|
|
33410
|
-
//# sourceMappingURL=deploy-engine-
|
|
33960
|
+
export { DEFAULT_STATE_PREFIX as $, resolveUseCdkBootstrapAssets as $n, __exportAll as $r, isSingleDynamicReferenceToken as $t, bold as A, validateAssetBucketName as An, LocalStartServiceError as Ar, requireConfigObject as At, secretBearingStateKeyWarning as B, runDockerStreaming as Bn, StateError as Br, DiffCalculator as Bt, unsupportedFinalSnapshotError as C, BOOTSTRAP_MARKER_PREFIX as Cn, ConfigError as Cr, assertRegionMatch as Ct, isStatefulRecreateTargetSync as D, isCrossRegionRedirect as Dn, DynamicReferenceRegionAmbiguousError as Dr, readConfigString as Dt, MULTI_REGION_RECREATE_BLOCKED_TYPES as E, getBootstrapMarkerKey as En, DeployCancelledError as Er, configStringRefusal as Et, yellow as F, dockerSpawnEnvWithSensitive as Fn, ProvisioningError as Fr, s3BucketDomainName as Ft, clearOnUpdateRemoval as G, getDefaultStateBucketName as Gn, withErrorHandling as Gr, TemplateParser as Gt, getCurrentResourceSecrets as H, getDockerImageBySourceHash as Hn, formatError as Hr, describeTypeWithThrottleRetry as Ht, collectDeclaredOutputNames as I, formatDockerLoginError as In, ResourceTimeoutError as Ir, s3BucketDualStackDomainName as It, findSilentDropProperties as J, resolveAutoAssetStorage as Jn, isThrottlingError as Jr, TEMPLATE_SOURCED_RULES as Jt, ProviderRegistry as K, getLegacyStateBucketName as Kn, isMarkedNonRetryable as Kr, STATE_SOURCED_CROSS_GENERATION_RULES as Kt, collectPublishedOutputNames as L, getDockerCmd as Ln, ResourceUpdateNotSupportedError as Lr, s3BucketRegionalDomainName as Lt, gray as M, buildDenyExternalAccessPolicy as Mn, MissingCdkCliError as Mr, classifyReplaySecretRegion as Mt, green as N, describeAwsFailure as Nn, NestedStackChildDirectDestroyError as Nr, producerRegionsFromState as Nt, renderStatefulReason as O, parseBootstrapMarker as On, LocalInvokeBuildError as Or, replayWarn as Ot, red as P, buildDockerImage as Pn, PartialFailureError as Pr, s3BucketArn as Pt, CUSTOM_RESOURCE_RESPONSE_PREFIX as Q, resolveStateBucketWithDefaultAndSource as Qn, retryClassificationText as Qr, errorCauseChain as Qt, exportAliasCollisionScrubWarning as R, partitionSensitiveEnv as Rn, StackHasActiveImportsError as Rr, s3BucketWebsiteUrl as Rt, refusesFinalSnapshot as S, AssetModeResolver as Sn, CdkdError as Sr, resolveExplicitPhysicalId as St, extractDeploymentEventError as T, ensureAssetStorage as Tn, DependencyError as Tr, configBooleanRefusal as Tt, IAMRoleProvider as U, Synthesizer as Un, isCdkdError as Ur, withRetry as Ut, stateKeySecretExposure as V, AssetManifestLoader as Vn, SynthesisError as Vr, INTRINSIC_KEYS as Vt, collectInlinePolicyNamesManagedBySiblings as W, synthesisStatusMessage as Wn, normalizeAwsError as Wr, DagBuilder as Wt, maskDeep as X, resolveSkipPrefix as Xn, markNonRetryable as Xr, createSecretMasker as Xt, createMaskedRetryLogger as Y, resolveCaptureObservedState as Yn, isTransientServerError as Yr, carriesSecretMask as Yt, maskerOrIdentity as Z, resolveStateBucketWithDefault as Zn, markRedactedCause as Zr, dynamicReferenceTokens as Zt, PRE_DELETE_SNAPSHOT_TYPES as _, createAssetRedirectResolver as _n, AwsClients as _r, isUnboundTemplateParameter as _t, DeploymentEventsStore as a, scrubResourceRecord as an, findLargeInlineResources as ar, CloudControlProvider as at, createPreDeleteFinalSnapshot as b, escapeRegExp$1 as bn, setAwsClients as br, WAFv2WebACLProvider as bt, replayFailedOperations as c, rebuildClientForBucketRegion as cn, displaySafe as cr, deleteIndeterminateGuards as ct, updatePartialReason as d, importableOutputs as dn, canonicalizeRegion as dr, isTerminationProtectionPropagationError as dt, maskSecretsInError as en, stateBucketExistenceConfirmed as er, beginCommandInterruptScope as et, withResourceDeadline as f, shouldRetainResource as fn, derivePartitionAndUrlSuffix as fr, IntrinsicFunctionResolver as ft, ATOMIC_FINAL_SNAPSHOT_TYPES as g, buildAssetRedirectMap as gn, resolveBucketRegion as gr, getAccountInfo as gt, computeImplicitDeleteEdges as h, WorkGraph as hn, clearBucketRegionCache as hr, coerceParameterTypedValue as ht, DeploymentEventsReader as i, redactSecretsForState as in, MIGRATE_TMP_PREFIX as ir, startInterruptWatch as it, cyan as j, validateContainerRepoName as jn, LockError as jr, requireConfigString as jt, formatResourceLine as k, readBootstrapMarkerBody as kn, LocalMigrateError as kr, requireConfigArray as kt, replayRollback as l, exportNamesCarriedFrom as ln, expectedOwnerParam as lr, deleteSkipReason as lt, IMPLICIT_DELETE_DEPENDENCIES as m, stringifyValue as mn, processStackMessages as mr, cfnRefValueFromPhysicalId as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, recordMaskOnlyValue as nn, CFN_TEMPLATE_BODY_LIMIT as nr, interruptWatchListenerCount as nt, planFailedOps as o, LockManager as on, uploadCfnTemplate as or, slowCcOperationTimeoutMs as ot, maskingRetryLogger as p, AssetPublisher as pn, AssemblyReader as pr, carriesDynamicReference as pt, findActionableSilentDrops as q, resolveApp as qn, isRetryableTransientError as qr, STATE_SOURCED_READBACK_RULES as qt, DeployEngine as r, recoverMaskedOutput as rn, CFN_TEMPLATE_URL_LIMIT as rr, isInterruptedWaitError as rt, planRollback as s, S3StateBackend as sn, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as sr, UNSPECIFIED_SKIP_REASON as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, maskSecretsInText as tn, warnDeprecatedNoPrefixCliFlag as tr, endCommandInterruptScope as tt, updatePartialMessage as u, importableOutputKeys as un, PARTITION_TABLE as ur, disableInstanceApiTermination as ut, buildFinalSnapshotIdentifier as v, loadPublishableAssetManifest as vn, getAwsClients as vr, parameterTypeMayLoseSecretIdentity as vt, makeCanonicalizePropertiesFn as w, assertAssetBucketRegion as wn, CrossAccountSecretRefusalError as wr, coerceCfnBoolean as wt, isFinalSnapshotError as x, stripControlChars as xn, AssetError as xr, normalizeAwsTagsToCfn as xt, ccRoutedFinalSnapshotError as y, rewriteTemplateAssetReferences as yn, resetAwsClients as yr, refStateLookupFromResource as yt, isExportAliasCollision as z, runDockerForeground as zn, StackTerminationProtectionError as zr, applyRoleArnIfSet as zt };
|
|
33961
|
+
//# sourceMappingURL=deploy-engine-BhBR2d3w.js.map
|