@go-to-k/cdkd 0.285.13 → 0.285.14
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-DcFgnQQO.js → asg-provider-BvIH9Ivw.js} +2 -2
- package/dist/{asg-provider-DcFgnQQO.js.map → asg-provider-BvIH9Ivw.js.map} +1 -1
- package/dist/cli.js +2 -2
- package/dist/{deploy-engine-DhMm2M33.js → deploy-engine-CA50haPO.js} +273 -49
- package/dist/{deploy-engine-DhMm2M33.js.map → deploy-engine-CA50haPO.js.map} +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/{program-DHJ4G_ZZ.js → program-BSanOkkV.js} +60 -72
- package/dist/{program-DHJ4G_ZZ.js.map → program-BSanOkkV.js.map} +1 -1
- package/dist/{version-FLW4rE5A.js → version-DP5kGlzj.js} +2 -2
- package/dist/{version-FLW4rE5A.js.map → version-DP5kGlzj.js.map} +1 -1
- package/package.json +1 -7
|
@@ -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-DP5kGlzj.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";
|
|
@@ -9608,6 +9608,99 @@ var LockManager = class {
|
|
|
9608
9608
|
/** Fixed marker substituted for a secret value in log / error output. */
|
|
9609
9609
|
const SECRET_MASK = "***";
|
|
9610
9610
|
/**
|
|
9611
|
+
* The UNCOLLAPSED companion of a {@link RecordedSecretValues} map: for each map
|
|
9612
|
+
* instance, every `expression -> plaintext` pair the resolver recorded INTO IT,
|
|
9613
|
+
* keyed by EXPRESSION (issue [#2485](https://github.com/go-to-k/cdkd/issues/2485)).
|
|
9614
|
+
*
|
|
9615
|
+
* WHY IT EXISTS. The map is keyed by PLAINTEXT, so two expressions resolving to
|
|
9616
|
+
* one value keep ONE entry — whichever the resolver recorded last. A WHOLE-token
|
|
9617
|
+
* leaf is immune (the position pass copies its own source), but a leaf that
|
|
9618
|
+
* EMBEDS a token in a literal string is redacted by the value scan, which can
|
|
9619
|
+
* only write the map's surviving expression: the versioned sibling's, for a
|
|
9620
|
+
* template that spells the un-versioned one, and the next deploy diffs that
|
|
9621
|
+
* leaf forever. Recovering the losing expression needs evidence the map has
|
|
9622
|
+
* discarded, and it has to be PASS-LOCAL: `recordedSecretExpressions` is
|
|
9623
|
+
* process-wide and says only that an expression IS secret, never what it
|
|
9624
|
+
* resolved to in THIS resource — so it cannot tell "the source token lost the
|
|
9625
|
+
* map slot to its sibling" from "the source token was never resolved here"
|
|
9626
|
+
* (a previous generation's bag, where writing today's expression over the
|
|
9627
|
+
* framed value would record something that was never deployed).
|
|
9628
|
+
*
|
|
9629
|
+
* Keyed by the map INSTANCE, so the evidence is exactly as pass-local as the
|
|
9630
|
+
* map itself: a map the resolver populated (the deploy's `perResourceSecrets`
|
|
9631
|
+
* entry, and equally the map drift / scrub / import hand their own resolution)
|
|
9632
|
+
* carries the pairs of THAT resolution, while a map the resolver did not
|
|
9633
|
+
* populate — a derived needle map, a nested-stack inheritance copy, a
|
|
9634
|
+
* `new Map(secrets)` copy — starts with no entries here and takes the
|
|
9635
|
+
* pre-#2485 fall-through, the safe direction. A copy loses the evidence
|
|
9636
|
+
* deliberately: a copy is not the pass that resolved anything.
|
|
9637
|
+
*
|
|
9638
|
+
* `CONFLICTING_PLAINTEXT` marks an expression this map saw resolve to TWO
|
|
9639
|
+
* values (a region-pinned re-resolution of one spelling, say); it then vouches
|
|
9640
|
+
* for nothing, which is the same "answer nothing you cannot prove" rule
|
|
9641
|
+
* {@link plaintextIndexOf} applies to the collapsed map's reverse index.
|
|
9642
|
+
*/
|
|
9643
|
+
const resolvedPairsOf = /* @__PURE__ */ new WeakMap();
|
|
9644
|
+
/**
|
|
9645
|
+
* Record that `expression` resolved to `plaintext` in the pass that owns
|
|
9646
|
+
* `secrets` — the resolver's recording seam calls this beside its
|
|
9647
|
+
* `secrets.set(plaintext, expression)`, so the two never disagree about which
|
|
9648
|
+
* pass the evidence belongs to. Mask-only map entries (value `SECRET_MASK`)
|
|
9649
|
+
* never pass through that seam — they came from no `{{resolve:...}}` token —
|
|
9650
|
+
* so nothing here special-cases the mask string: a secret whose plaintext
|
|
9651
|
+
* happens to BE `***` is a secret like any other.
|
|
9652
|
+
*/
|
|
9653
|
+
function recordResolvedPair(secrets, expression, plaintext) {
|
|
9654
|
+
let pairs = resolvedPairsOf.get(secrets);
|
|
9655
|
+
if (pairs === void 0) {
|
|
9656
|
+
pairs = /* @__PURE__ */ new Map();
|
|
9657
|
+
resolvedPairsOf.set(secrets, pairs);
|
|
9658
|
+
}
|
|
9659
|
+
const previous = pairs.get(expression);
|
|
9660
|
+
if (previous === void 0) pairs.set(expression, plaintext);
|
|
9661
|
+
else if (previous !== plaintext) pairs.set(expression, CONFLICTING_PLAINTEXT);
|
|
9662
|
+
}
|
|
9663
|
+
/**
|
|
9664
|
+
* Carry the resolved pairs of `from` into `to`, for the one copy of a
|
|
9665
|
+
* resolver-populated map that POSITIONS anything: the deploy engine accumulates
|
|
9666
|
+
* each stack's output resolution into its `outputSecrets` bag entry by entry,
|
|
9667
|
+
* and without this the copy would keep the collapsed entries while dropping the
|
|
9668
|
+
* evidence — so a literal `Output` embedding one of two same-plaintext
|
|
9669
|
+
* references would fall back to the value scan and persist the sibling's
|
|
9670
|
+
* expression. The engine's other entry-by-entry copy — an `Export.Name`'s
|
|
9671
|
+
* secrets into the pass map — deliberately does NOT call this: a name never
|
|
9672
|
+
* positions a leaf, a value re-using the same token records its own pair at
|
|
9673
|
+
* the seam, and the only thing the merge could add is a CONFLICT (a
|
|
9674
|
+
* non-cacheable `{{resolve:ssm:X}}` whose value moved between the value pass
|
|
9675
|
+
* and the name's resolution), which would destroy positioning the value pass
|
|
9676
|
+
* had earned. A pair that conflicts across the two maps is marked conflicting
|
|
9677
|
+
* in `to`, the same rule {@link recordResolvedPair} applies within one map.
|
|
9678
|
+
*
|
|
9679
|
+
* Deliberately NOT a general "copy the map" helper: every other new map is a
|
|
9680
|
+
* different PASS, and starting it without evidence is the safe direction.
|
|
9681
|
+
*/
|
|
9682
|
+
function mergeResolvedPairs(from, to) {
|
|
9683
|
+
const pairs = resolvedPairsOf.get(from);
|
|
9684
|
+
if (pairs === void 0) return;
|
|
9685
|
+
for (const [expression, plaintext] of pairs) if (typeof plaintext === "string") recordResolvedPair(to, expression, plaintext);
|
|
9686
|
+
else {
|
|
9687
|
+
let target = resolvedPairsOf.get(to);
|
|
9688
|
+
if (target === void 0) {
|
|
9689
|
+
target = /* @__PURE__ */ new Map();
|
|
9690
|
+
resolvedPairsOf.set(to, target);
|
|
9691
|
+
}
|
|
9692
|
+
target.set(expression, CONFLICTING_PLAINTEXT);
|
|
9693
|
+
}
|
|
9694
|
+
}
|
|
9695
|
+
/**
|
|
9696
|
+
* The plaintext `expression` resolved to in the pass that owns `secrets`, or
|
|
9697
|
+
* `undefined` when that pass recorded nothing for it (or two different values).
|
|
9698
|
+
*/
|
|
9699
|
+
function resolvedPlaintextOf(secrets, expression) {
|
|
9700
|
+
const recorded = resolvedPairsOf.get(secrets)?.get(expression);
|
|
9701
|
+
return typeof recorded === "string" ? recorded : void 0;
|
|
9702
|
+
}
|
|
9703
|
+
/**
|
|
9611
9704
|
* Every `{{resolve:...}}` expression this process has PROVEN resolves to a
|
|
9612
9705
|
* secret, as a SET — uncollapsed by resolved value (issue #1910).
|
|
9613
9706
|
*
|
|
@@ -10618,8 +10711,9 @@ function isKnownSecretExpression(expression, secretExpressions) {
|
|
|
10618
10711
|
return isSecretExpressionByVerdictOrSpelling(expression) || secretExpressions.has(expression);
|
|
10619
10712
|
}
|
|
10620
10713
|
/**
|
|
10621
|
-
* The
|
|
10622
|
-
* `secretsmanager` by SPELLING, and anything this process
|
|
10714
|
+
* The arms of {@link isKnownSecretExpression} that need NO pass-local set:
|
|
10715
|
+
* `secretsmanager` / `ssm-secure` by SPELLING, and anything this process
|
|
10716
|
+
* PROVED secret.
|
|
10623
10717
|
*
|
|
10624
10718
|
* Split out so the resolver can ask the same question at the issue #2059
|
|
10625
10719
|
* recording seam, where no `secretExpressions` set is in hand. It must not
|
|
@@ -10644,7 +10738,7 @@ function isKnownSecretExpression(expression, secretExpressions) {
|
|
|
10644
10738
|
* change to a store this function only reads.
|
|
10645
10739
|
*/
|
|
10646
10740
|
function isSecretExpressionByVerdictOrSpelling(expression) {
|
|
10647
|
-
return expression.startsWith("{{resolve:secretsmanager:") || isRecordedSecretExpression(expression);
|
|
10741
|
+
return expression.startsWith("{{resolve:secretsmanager:") || expression.startsWith("{{resolve:ssm-secure:") || isRecordedSecretExpression(expression);
|
|
10648
10742
|
}
|
|
10649
10743
|
/**
|
|
10650
10744
|
* The character class a `{{resolve:...}}` reference's INNER text is built from,
|
|
@@ -11188,6 +11282,124 @@ function positionByIntrinsicSkeleton(bag, source, secrets, secretExpressions) {
|
|
|
11188
11282
|
return matched;
|
|
11189
11283
|
}
|
|
11190
11284
|
/**
|
|
11285
|
+
* The ONE-span frame shared by {@link positionByEmbeddedSpan} and
|
|
11286
|
+
* {@link learnMixedLeafNeedle}: a source holding exactly one `{{resolve:...}}`
|
|
11287
|
+
* token, and a bag that starts with the source's prefix and ends with its
|
|
11288
|
+
* suffix with something non-empty between them that is NOT itself a complete
|
|
11289
|
+
* token (an already-redacted record is a persisted answer, not a plaintext).
|
|
11290
|
+
* `undefined` for any other shape. One helper rather than two copies so the
|
|
11291
|
+
* two refusals cannot drift apart.
|
|
11292
|
+
*/
|
|
11293
|
+
function singleSpanFrame(bag, source) {
|
|
11294
|
+
const spans = dynamicReferenceSpans(source);
|
|
11295
|
+
if (spans.length !== 1) return void 0;
|
|
11296
|
+
const [span] = spans;
|
|
11297
|
+
const token = source.slice(span.start, span.end);
|
|
11298
|
+
const prefix = source.slice(0, span.start);
|
|
11299
|
+
const suffix = source.slice(span.end);
|
|
11300
|
+
if (bag.length <= prefix.length + suffix.length) return void 0;
|
|
11301
|
+
if (!bag.startsWith(prefix) || !bag.endsWith(suffix)) return void 0;
|
|
11302
|
+
const middle = bag.slice(prefix.length, bag.length - suffix.length);
|
|
11303
|
+
if (isSingleDynamicReferenceToken(middle)) return void 0;
|
|
11304
|
+
return {
|
|
11305
|
+
token,
|
|
11306
|
+
prefix,
|
|
11307
|
+
suffix,
|
|
11308
|
+
middle
|
|
11309
|
+
};
|
|
11310
|
+
}
|
|
11311
|
+
/**
|
|
11312
|
+
* Position a literal source leaf that EMBEDS exactly one `{{resolve:...}}`
|
|
11313
|
+
* token — `postgres://app-svc:{{resolve:ssm-secure:NAME}}@db/app` — by the
|
|
11314
|
+
* span the source states, writing `prefix + token + suffix` (issue
|
|
11315
|
+
* [#2485](https://github.com/go-to-k/cdkd/issues/2485)).
|
|
11316
|
+
*
|
|
11317
|
+
* WHY THE VALUE SCAN IS NOT ENOUGH HERE. The scan writes the map's surviving
|
|
11318
|
+
* expression for a plaintext, and the map keeps one expression per plaintext:
|
|
11319
|
+
* a whole-value `NAME:1` sibling that resolved LAST leaves `NAME:1` as the only
|
|
11320
|
+
* expression for the value, so the embedded leaf persists the versioned
|
|
11321
|
+
* spelling for a template that spells `NAME`, and the deploy diff — expression
|
|
11322
|
+
* against expression — reports that leaf on every run. The whole-token arm of
|
|
11323
|
+
* {@link redactByPath} is immune because it copies its own source; this arm
|
|
11324
|
+
* gives the one-span literal leaf the same immunity.
|
|
11325
|
+
*
|
|
11326
|
+
* THE EVIDENCE, and why the shape of the frame is not enough on its own: the
|
|
11327
|
+
* frame check (`bag` starts with the source's prefix and ends with its suffix,
|
|
11328
|
+
* with something between) is what {@link learnMixedLeafNeedle} already uses to
|
|
11329
|
+
* LEARN a needle, and it proves only that the bag has the source's shape. The
|
|
11330
|
+
* bag can also be a PREVIOUS generation's (`cdkd scrub`, a state-sourced walk)
|
|
11331
|
+
* with an earlier plaintext framed exactly like this, and writing today's
|
|
11332
|
+
* token over it would record an expression that was never deployed at that
|
|
11333
|
+
* position — the hazard `sourceIsSameGeneration` exists for on the whole-token
|
|
11334
|
+
* arm. So the middle must EQUAL what THIS pass recorded the source token
|
|
11335
|
+
* resolving to ({@link recordResolvedPair}, per map instance): that is evidence
|
|
11336
|
+
* of this resolution, not of shape, and it is absent by construction for every
|
|
11337
|
+
* bag this pass did not produce. It is also what keeps a PUBLIC `ssm` token
|
|
11338
|
+
* resolved (issue #1901) — the resolver records only secret verdicts — and what
|
|
11339
|
+
* keeps a mask-only `NoEcho` value out (never recorded).
|
|
11340
|
+
*
|
|
11341
|
+
* WHAT THIS EVIDENCE DOES NOT CLAIM, stated because a reviewer asked: it does
|
|
11342
|
+
* not prove the bag was produced FROM this source. A previous generation's bag
|
|
11343
|
+
* whose framed middle happens to EQUAL a plaintext this pass resolved the
|
|
11344
|
+
* source token to (`cdkd scrub` walking an old record against today's template,
|
|
11345
|
+
* or a failed deploy persisting an old bag) takes this arm and persists TODAY's
|
|
11346
|
+
* expression at that position. That is not a new claim: the value scan the
|
|
11347
|
+
* arm replaces rewrites that same plaintext onto one of THIS pass's expressions
|
|
11348
|
+
* regardless of generation — the map holds no other — so the class of answer
|
|
11349
|
+
* is unchanged and only the choice within it improves (the source's own
|
|
11350
|
+
* token rather than the map's survivor). The generation hazard this arm must
|
|
11351
|
+
* not create is the whole-token arm's: a middle that is ALREADY an expression
|
|
11352
|
+
* (a persisted answer from another generation), which the token refusal below
|
|
11353
|
+
* keeps out — and, by the same argument, any leaf the value scan would NOT
|
|
11354
|
+
* rewrite to exactly `prefix + survivor + suffix`: a middle shorter than the
|
|
11355
|
+
* scan's needle floor (an embedded 1-3 character secret stays the scan's
|
|
11356
|
+
* documented residual — issue #2516 tracks closing it with a bound that
|
|
11357
|
+
* proves the bag's generation, which this evidence does not), a whole leaf
|
|
11358
|
+
* that is itself another recorded plaintext, a needle starting in the prefix
|
|
11359
|
+
* and overlapping the middle. The
|
|
11360
|
+
* arm checks that equivalence against the scan's own answer rather than
|
|
11361
|
+
* re-deriving the scan's rules. Pinned by the cross-generation cases in
|
|
11362
|
+
* `secret-redaction-embedded-span.test.ts`.
|
|
11363
|
+
*
|
|
11364
|
+
* One shape reaches this arm that a reader may not expect: a WHOLE-token
|
|
11365
|
+
* source that FAILED the whole-token arm's `isKnownSecretExpression` gate (an
|
|
11366
|
+
* `ssm` token whose type came back unclassifiable and which lost the map slot
|
|
11367
|
+
* to a sibling). Its "frame" is empty, and if this pass recorded it resolving
|
|
11368
|
+
* to the bag it is written back as itself — an expression, and the leaf's own,
|
|
11369
|
+
* where the scan wrote the survivor. Stated so it is not mistaken for a leak.
|
|
11370
|
+
*
|
|
11371
|
+
* Everything else keeps the pre-#2485 fall-through: two or more spans (which
|
|
11372
|
+
* span produced which value is genuinely ambiguous when they share one), an
|
|
11373
|
+
* `Fn::Sub` / `Fn::Join` source (an object, not this arm at all — issue #2320's
|
|
11374
|
+
* placeholder primitive), a frame mismatch, a middle that is itself a complete
|
|
11375
|
+
* token (an already-redacted record, per the same refusal
|
|
11376
|
+
* {@link learnMixedLeafNeedle} makes), and a middle this pass cannot vouch for.
|
|
11377
|
+
*
|
|
11378
|
+
* The frame is copied from the SOURCE, not scanned. A needle occurring in the
|
|
11379
|
+
* literal frame would be a reference the template never had at that offset —
|
|
11380
|
+
* the fabricated-baseline direction {@link preferPositionDecisions} refuses —
|
|
11381
|
+
* and the whole-token arm returns its source unscanned for the same reason.
|
|
11382
|
+
*
|
|
11383
|
+
* RETURNS THE VALUE SCAN'S ANSWER ON EVERY REFUSAL, not `undefined`: the scan
|
|
11384
|
+
* is computed once, here, for `(bag, secrets)` — the arm's bound below compares
|
|
11385
|
+
* against it, and every fall-through IS it — so the compared value provably
|
|
11386
|
+
* comes from the same bag and map the arm positions. An earlier revision took
|
|
11387
|
+
* the scan as a parameter, which left the bound one wrong caller away from
|
|
11388
|
+
* comparing against a scan of some other bag with no type error.
|
|
11389
|
+
*/
|
|
11390
|
+
function positionByEmbeddedSpan(bag, source, secrets) {
|
|
11391
|
+
const scanned = redactSecretsForState(bag, secrets);
|
|
11392
|
+
const frame = singleSpanFrame(bag, source);
|
|
11393
|
+
if (frame === void 0) return scanned;
|
|
11394
|
+
const { token, prefix, suffix, middle } = frame;
|
|
11395
|
+
const recorded = resolvedPlaintextOf(secrets, token);
|
|
11396
|
+
if (recorded === void 0 || recorded !== middle) return scanned;
|
|
11397
|
+
const survivor = secrets.get(middle);
|
|
11398
|
+
if (survivor === void 0) return scanned;
|
|
11399
|
+
if (scanned !== prefix + survivor + suffix) return scanned;
|
|
11400
|
+
return prefix + token + suffix;
|
|
11401
|
+
}
|
|
11402
|
+
/**
|
|
11191
11403
|
* Keys tried, in order, when pairing two arrays whose ORDER cannot be trusted
|
|
11192
11404
|
* (issue #1915).
|
|
11193
11405
|
*
|
|
@@ -11387,7 +11599,7 @@ function redactByPath(bag, source, secrets, rules, secretExpressions) {
|
|
|
11387
11599
|
if (!rules.sourceIsSameGeneration && isSingleDynamicReferenceToken(bag)) return secrets.get(bag) ?? bag;
|
|
11388
11600
|
return source;
|
|
11389
11601
|
}
|
|
11390
|
-
return
|
|
11602
|
+
return positionByEmbeddedSpan(bag, source, secrets);
|
|
11391
11603
|
}
|
|
11392
11604
|
if (typeof bag === "string" && isPlainObject$2(source)) {
|
|
11393
11605
|
const certified = positionByCrossStackSource(bag, source, secrets);
|
|
@@ -11505,12 +11717,13 @@ function subtreeHasDynamicReference(value) {
|
|
|
11505
11717
|
* Widening it changes one answer, in the SAFE direction for BOTH readers.
|
|
11506
11718
|
*
|
|
11507
11719
|
* `drift.ts`'s `survivingDynamicReferences` is the reader that is easy to
|
|
11508
|
-
* forget, because it lives in another file — it feeds
|
|
11509
|
-
*
|
|
11510
|
-
*
|
|
11511
|
-
*
|
|
11512
|
-
*
|
|
11513
|
-
* `
|
|
11720
|
+
* forget, because it lives in another file — it feeds the survivor REPORT
|
|
11721
|
+
* (`onUnresolved`, and through it the `unresolvedToken` cause), so seeing MORE
|
|
11722
|
+
* tokens can only report more, never less. Do not shorten this to "the only
|
|
11723
|
+
* reader": that sentence is what a later editor uses to bound the blast
|
|
11724
|
+
* radius of touching the class, and getting it wrong points them away from
|
|
11725
|
+
* the report / `--json` / `--accept` path where an unreported survivor would
|
|
11726
|
+
* surface.
|
|
11514
11727
|
*
|
|
11515
11728
|
* The other reader is the DECLARED direction for issue #1901:
|
|
11516
11729
|
* {@link mixedLeafMayCarryPublicReference}, which asks whether a MIXED leaf
|
|
@@ -11916,9 +12129,9 @@ function unkeyedArrayPairsByAnchors(bag, source) {
|
|
|
11916
12129
|
* from one the pass decided IN FAVOUR of the value already there. Two shapes
|
|
11917
12130
|
* hit it, both fabricating a baseline `cdkd drift --revert` then pushes:
|
|
11918
12131
|
*
|
|
11919
|
-
* - the resolver's unsupported-service arm leaves
|
|
11920
|
-
*
|
|
11921
|
-
* leaf. The string arm returns `source` — a decision — and the equality made
|
|
12132
|
+
* - the resolver's unsupported-service arm leaves a `{{resolve:...}}` token it
|
|
12133
|
+
* has no arm for LITERAL (`ssm-secure:` was one until issue #2482), so AWS
|
|
12134
|
+
* echoes it back and the source leaf EQUALS the bag leaf. The string arm returns `source` — a decision — and the equality made
|
|
11922
12135
|
* it look like no decision at all. (A BARE such token takes the whole-token
|
|
11923
12136
|
* arm and one embedded in text takes the mixed-leaf arm; both decide, and
|
|
11924
12137
|
* both were misread.)
|
|
@@ -12102,7 +12315,8 @@ function learnWholeTokenNeedle(collector, bag, source) {
|
|
|
12102
12315
|
* would then contain a whole `{{resolve:...}}` token and a resolved readback
|
|
12103
12316
|
* cannot end with one. The shape it genuinely decides is a second reference
|
|
12104
12317
|
* that survives LITERALLY in the readback — the resolver's
|
|
12105
|
-
* unsupported-service arm (`ssm-secure:`
|
|
12318
|
+
* unsupported-service arm produces exactly that (`ssm-secure:` did until
|
|
12319
|
+
* issue #2482; a spelling with no arm still does) — where the
|
|
12106
12320
|
* extraction would in fact be right and is declined anyway. Measured: a
|
|
12107
12321
|
* both-resolved fixture leaves this line unfenced.
|
|
12108
12322
|
* - the source's literal PREFIX and SUFFIX must both be present at the ends of
|
|
@@ -12116,17 +12330,10 @@ function learnWholeTokenNeedle(collector, bag, source) {
|
|
|
12116
12330
|
* correctly, while an `indexOf` scan would cut it short.
|
|
12117
12331
|
*/
|
|
12118
12332
|
function learnMixedLeafNeedle(collector, bag, source) {
|
|
12119
|
-
const
|
|
12120
|
-
if (
|
|
12121
|
-
const
|
|
12122
|
-
const token = source.slice(span.start, span.end);
|
|
12333
|
+
const frame = singleSpanFrame(bag, source);
|
|
12334
|
+
if (frame === void 0) return;
|
|
12335
|
+
const { token, middle: plaintext } = frame;
|
|
12123
12336
|
if (!expressionMaySeedANeedle(token)) return;
|
|
12124
|
-
const prefix = source.slice(0, span.start);
|
|
12125
|
-
const suffix = source.slice(span.end);
|
|
12126
|
-
if (bag.length <= prefix.length + suffix.length) return;
|
|
12127
|
-
if (!bag.startsWith(prefix) || !bag.endsWith(suffix)) return;
|
|
12128
|
-
const plaintext = bag.slice(prefix.length, bag.length - suffix.length);
|
|
12129
|
-
if (isSingleDynamicReferenceToken(plaintext)) return;
|
|
12130
12337
|
learnNeedle(collector, plaintext, token);
|
|
12131
12338
|
}
|
|
12132
12339
|
/**
|
|
@@ -15382,17 +15589,22 @@ function s3BucketWebsiteUrl(bucketName, region) {
|
|
|
15382
15589
|
/**
|
|
15383
15590
|
* The `{{resolve:<service>:...}}` families whose value can be a SECRET, and
|
|
15384
15591
|
* therefore the only ones the region question below is asked about: every
|
|
15385
|
-
* `secretsmanager` reference by spelling,
|
|
15386
|
-
*
|
|
15387
|
-
*
|
|
15388
|
-
*
|
|
15389
|
-
*
|
|
15390
|
-
*
|
|
15391
|
-
*
|
|
15392
|
-
*
|
|
15393
|
-
*
|
|
15394
|
-
|
|
15395
|
-
|
|
15592
|
+
* `secretsmanager` reference by spelling, every `ssm-secure` one by spelling
|
|
15593
|
+
* (issue #2482 — it is resolved through the same `GetParameter` as `ssm`, so
|
|
15594
|
+
* the wrong region answers it in exactly the same way), and every `ssm` one,
|
|
15595
|
+
* which is secret exactly when its parameter is a `SecureString` (issue #1901).
|
|
15596
|
+
*
|
|
15597
|
+
* Every OTHER service is `local` because cdkd cannot resolve it at all — the
|
|
15598
|
+
* resolver's unsupported-service arm leaves such a token in place, so there
|
|
15599
|
+
* is no lookup for a region to get wrong. None of CloudFormation's three
|
|
15600
|
+
* services is in that position any more; the arm exists for a spelling that
|
|
15601
|
+
* is not a dynamic reference at all, or one AWS adds later.
|
|
15602
|
+
*/
|
|
15603
|
+
const REPLAY_SECRET_SERVICES = /* @__PURE__ */ new Set([
|
|
15604
|
+
"secretsmanager",
|
|
15605
|
+
"ssm",
|
|
15606
|
+
"ssm-secure"
|
|
15607
|
+
]);
|
|
15396
15608
|
/**
|
|
15397
15609
|
* Split a `{{resolve:secretsmanager:...}}` inner body into its SECRET_ID.
|
|
15398
15610
|
*
|
|
@@ -15428,7 +15640,7 @@ function secretsManagerSecretId(inner) {
|
|
|
15428
15640
|
* a different thing in the refusal message than the one that would be read.
|
|
15429
15641
|
*/
|
|
15430
15642
|
function ssmParameterName(inner) {
|
|
15431
|
-
return inner.substring(
|
|
15643
|
+
return inner.substring(inner.indexOf(":") + 1);
|
|
15432
15644
|
}
|
|
15433
15645
|
/**
|
|
15434
15646
|
* The region an ARN names, or `undefined` for anything that is not an ARN with
|
|
@@ -20488,7 +20700,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
20488
20700
|
});
|
|
20489
20701
|
for (const { fullMatch, inner } of matches) {
|
|
20490
20702
|
const service = inner.split(":")[0];
|
|
20491
|
-
const isKnownSecret = service === "secretsmanager" || recordedSecretExpressions.has(fullMatch);
|
|
20703
|
+
const isKnownSecret = service === "secretsmanager" || service === "ssm-secure" || recordedSecretExpressions.has(fullMatch);
|
|
20492
20704
|
if (isKnownSecret && context?.skipDynamicReferences) continue;
|
|
20493
20705
|
const regionVerdict = classifyReplaySecretRegion(fullMatch, this.explicitRegion ?? this.resolverRegion, context?.producerRegions);
|
|
20494
20706
|
if (regionVerdict.kind === "ambiguous") throw markNonRetryable(new DynamicReferenceRegionAmbiguousError(`Refusing to resolve the secret reference ${fullMatch}: it names '${regionVerdict.secretName}' without a region, and this stack reads from ${regionVerdict.foreignProducerRegions.join(", ")} as well as its own region. cdkd cannot tell which one must answer, and resolving against the wrong one yields a different secret. Spell the reference as a full ARN to say which region owns it.`));
|
|
@@ -20499,7 +20711,10 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
20499
20711
|
}
|
|
20500
20712
|
const cached = this.cachedDynamicReferences.get(fullMatch);
|
|
20501
20713
|
if (cached) {
|
|
20502
|
-
if (cached.secret && cached.value)
|
|
20714
|
+
if (cached.secret && cached.value) {
|
|
20715
|
+
context?.recordedSecretValues?.set(cached.value, fullMatch);
|
|
20716
|
+
if (context?.recordedSecretValues) recordResolvedPair(context.recordedSecretValues, fullMatch, cached.value);
|
|
20717
|
+
}
|
|
20503
20718
|
result = result.replace(fullMatch, () => cached.value);
|
|
20504
20719
|
continue;
|
|
20505
20720
|
}
|
|
@@ -20531,6 +20746,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
20531
20746
|
if (!decrypt) continue;
|
|
20532
20747
|
}
|
|
20533
20748
|
resolved = param.value;
|
|
20749
|
+
} else if (service === "ssm-secure") {
|
|
20750
|
+
const param = await this.resolveSSMReference(parts, true, "ssm-secure");
|
|
20751
|
+
if (!param.secure) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Refusing to resolve ${fullMatch}: the parameter is a ${param.type} parameter, and the ssm-secure spelling is defined for SecureString parameters only. Reference it as {{resolve:ssm:...}} if it is public configuration.`));
|
|
20752
|
+
isSecret = true;
|
|
20753
|
+
resolved = param.value;
|
|
20534
20754
|
} else {
|
|
20535
20755
|
this.logger.warn(`Unsupported dynamic reference service: ${service}`);
|
|
20536
20756
|
continue;
|
|
@@ -20541,7 +20761,8 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
20541
20761
|
});
|
|
20542
20762
|
if (isSecret && resolved) {
|
|
20543
20763
|
context?.recordedSecretValues?.set(resolved, fullMatch);
|
|
20544
|
-
if (
|
|
20764
|
+
if (context?.recordedSecretValues) recordResolvedPair(context.recordedSecretValues, fullMatch, resolved);
|
|
20765
|
+
if (service === "secretsmanager" || service === "ssm-secure") this.pinSecretVerdict(fullMatch, true);
|
|
20545
20766
|
}
|
|
20546
20767
|
result = result.replace(fullMatch, () => resolved);
|
|
20547
20768
|
}
|
|
@@ -20751,16 +20972,16 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
20751
20972
|
* discard the value when `secure` is set — it is ciphertext, not the resolved
|
|
20752
20973
|
* reference.
|
|
20753
20974
|
*/
|
|
20754
|
-
async resolveSSMReference(parts, decrypt = true) {
|
|
20975
|
+
async resolveSSMReference(parts, decrypt = true, service = "ssm") {
|
|
20755
20976
|
const parameterName = parts.slice(1).join(":");
|
|
20756
|
-
if (!parameterName) throw new Error(
|
|
20757
|
-
this.logger.debug(`Resolving dynamic reference:
|
|
20977
|
+
if (!parameterName) throw new Error(`Dynamic reference: ${service} PARAMETER_NAME is required`);
|
|
20978
|
+
this.logger.debug(`Resolving dynamic reference: ${service}:${parameterName}`);
|
|
20758
20979
|
const client = this.clientsForRegion(this.explicitRegion).ssm;
|
|
20759
20980
|
const command = new GetParameterCommand({
|
|
20760
20981
|
Name: parameterName,
|
|
20761
20982
|
WithDecryption: decrypt
|
|
20762
20983
|
});
|
|
20763
|
-
const response = await this.sendWithThrottleRetry(() => client.send(command),
|
|
20984
|
+
const response = await this.sendWithThrottleRetry(() => client.send(command), `${service}:${parameterName}`);
|
|
20764
20985
|
const paramValue = response.Parameter?.Value;
|
|
20765
20986
|
if (paramValue === void 0 || paramValue === null) throw new Error(`Dynamic reference: SSM parameter '${parameterName}' not found or has no value`);
|
|
20766
20987
|
const paramType = response.Parameter?.Type;
|
|
@@ -20768,7 +20989,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
20768
20989
|
if (secure && paramType !== "SecureString" && !this.warnedUnrecognizedSsmTypes.has(`${parameterName}\u0000${String(paramType)}`)) {
|
|
20769
20990
|
this.warnedUnrecognizedSsmTypes.add(`${parameterName}\u0000${String(paramType)}`);
|
|
20770
20991
|
const reported = paramType === void 0 ? "(absent)" : `'${String(paramType)}'`;
|
|
20771
|
-
this.logger.warn(`SSM parameter '${parameterName}' reported an unrecognized Type ${reported} — treating its value as a secret, so cdkd will persist the {{resolve
|
|
20992
|
+
this.logger.warn(`SSM parameter '${parameterName}' reported an unrecognized Type ${reported} — treating its value as a secret, so cdkd will persist the {{resolve:${service}:...}} expression rather than the resolved value. Declare the parameter as String / StringList if it is public config.`);
|
|
20772
20993
|
}
|
|
20773
20994
|
return {
|
|
20774
20995
|
value: paramValue,
|
|
@@ -21903,7 +22124,7 @@ var CloudControlProvider = class {
|
|
|
21903
22124
|
const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
|
|
21904
22125
|
if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
|
|
21905
22126
|
this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
|
|
21906
|
-
const { ASGProvider } = await import("./asg-provider-
|
|
22127
|
+
const { ASGProvider } = await import("./asg-provider-BvIH9Ivw.js").then((n) => n.n);
|
|
21907
22128
|
return withIndeterminateGuard(await new ASGProvider().delete(logicalId, physicalId, resourceType, _properties, context), indeterminateGuard);
|
|
21908
22129
|
}
|
|
21909
22130
|
const isProtectedEc2Instance = context?.removeProtection === true && resourceType === "AWS::EC2::Instance";
|
|
@@ -34007,7 +34228,10 @@ var DeployEngine = class {
|
|
|
34007
34228
|
}
|
|
34008
34229
|
outputsPassCompleted = true;
|
|
34009
34230
|
} finally {
|
|
34010
|
-
if (context.recordedSecretValues)
|
|
34231
|
+
if (context.recordedSecretValues) {
|
|
34232
|
+
for (const [value, expr] of context.recordedSecretValues) this.outputSecrets.set(value, expr);
|
|
34233
|
+
mergeResolvedPairs(context.recordedSecretValues, this.outputSecrets);
|
|
34234
|
+
}
|
|
34011
34235
|
if (!outputsPassCompleted) this.outputsSourceUsable = false;
|
|
34012
34236
|
}
|
|
34013
34237
|
for (const [outputKey, output] of Object.entries(template.Outputs)) {
|
|
@@ -34028,5 +34252,5 @@ var DeployEngine = class {
|
|
|
34028
34252
|
};
|
|
34029
34253
|
|
|
34030
34254
|
//#endregion
|
|
34031
|
-
export { DEFAULT_STATE_PREFIX as $, resolveUseCdkBootstrapAssets as $n,
|
|
34032
|
-
//# sourceMappingURL=deploy-engine-
|
|
34255
|
+
export { DEFAULT_STATE_PREFIX as $, resolveUseCdkBootstrapAssets as $n, retryClassificationText as $r, isSingleDynamicReferenceToken as $t, bold as A, validateAssetBucketName as An, LocalMigrateError as Ar, requireConfigObject as At, secretBearingStateKeyWarning as B, runDockerStreaming as Bn, StackTerminationProtectionError 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, PartialFailureError as Fr, s3BucketDomainName as Ft, clearOnUpdateRemoval as G, getDefaultStateBucketName as Gn, normalizeAwsError as Gr, TemplateParser as Gt, getCurrentResourceSecrets as H, getDockerImageBySourceHash as Hn, SynthesisError as Hr, describeTypeWithThrottleRetry as Ht, collectDeclaredOutputNames as I, formatDockerLoginError as In, ProvisioningError as Ir, s3BucketDualStackDomainName as It, findSilentDropProperties as J, resolveAutoAssetStorage as Jn, isRetryableTransientError as Jr, TEMPLATE_SOURCED_RULES as Jt, ProviderRegistry as K, getLegacyStateBucketName as Kn, withErrorHandling as Kr, STATE_SOURCED_CROSS_GENERATION_RULES as Kt, collectPublishedOutputNames as L, getDockerCmd as Ln, ResourceTimeoutError as Lr, s3BucketRegionalDomainName as Lt, gray as M, buildDenyExternalAccessPolicy as Mn, LockError as Mr, classifyReplaySecretRegion as Mt, green as N, describeAwsFailure as Nn, MissingCdkCliError as Nr, producerRegionsFromState as Nt, renderStatefulReason as O, parseBootstrapMarker as On, IntrinsicResolutionRefusalError as Or, replayWarn as Ot, red as P, buildDockerImage as Pn, NestedStackChildDirectDestroyError as Pr, s3BucketArn as Pt, CUSTOM_RESOURCE_RESPONSE_PREFIX as Q, resolveStateBucketWithDefaultAndSource as Qn, markRedactedCause as Qr, errorCauseChain as Qt, exportAliasCollisionScrubWarning as R, partitionSensitiveEnv as Rn, ResourceUpdateNotSupportedError 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, formatError as Ur, withRetry as Ut, stateKeySecretExposure as V, AssetManifestLoader as Vn, StateError as Vr, INTRINSIC_KEYS as Vt, collectInlinePolicyNamesManagedBySiblings as W, synthesisStatusMessage as Wn, isCdkdError as Wr, DagBuilder as Wt, maskDeep as X, resolveSkipPrefix as Xn, isTransientServerError as Xr, createSecretMasker as Xt, createMaskedRetryLogger as Y, resolveCaptureObservedState as Yn, isThrottlingError as Yr, carriesSecretMask as Yt, maskerOrIdentity as Z, resolveStateBucketWithDefault as Zn, markNonRetryable 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, __exportAll as ei, 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, LocalStartServiceError as jr, requireConfigString as jt, formatResourceLine as k, readBootstrapMarkerBody as kn, LocalInvokeBuildError 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, isMarkedNonRetryable 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, StackHasActiveImportsError as zr, applyRoleArnIfSet as zt };
|
|
34256
|
+
//# sourceMappingURL=deploy-engine-CA50haPO.js.map
|