@go-to-k/cdkd 0.302.1 → 0.302.3

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.
Files changed (31) hide show
  1. package/dist/{asg-provider-BpRcm_UB.js → asg-provider-DCrwgSCZ.js} +27 -9
  2. package/dist/asg-provider-DCrwgSCZ.js.map +1 -0
  3. package/dist/cli.js +2 -2
  4. package/dist/{deploy-engine-CYVt2NQR.js → deploy-engine-D9SDE-Pj.js} +359 -4269
  5. package/dist/deploy-engine-D9SDE-Pj.js.map +1 -0
  6. package/dist/{iam-policy-targets-CmHNH1DY.js → iam-policy-targets-OtGPguLH.js} +3 -2
  7. package/dist/{iam-policy-targets-CmHNH1DY.js.map → iam-policy-targets-OtGPguLH.js.map} +1 -1
  8. package/dist/{iam-role-provider-R1GC6KcT.js → iam-role-provider-Q1sv3tF1.js} +9 -5
  9. package/dist/iam-role-provider-Q1sv3tF1.js.map +1 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +3 -3
  12. package/dist/{neptune-provider-DzTl6-74.js → neptune-provider-DjVgEWGf.js} +121 -11
  13. package/dist/neptune-provider-DjVgEWGf.js.map +1 -0
  14. package/dist/{program-CvRs5pV_.js → program-DSbJoNKe.js} +559 -305
  15. package/dist/program-DSbJoNKe.js.map +1 -0
  16. package/dist/{provider-classes-B2_u4Fg0.js → provider-classes-CDtquwkU.js} +673 -131
  17. package/dist/provider-classes-CDtquwkU.js.map +1 -0
  18. package/dist/{rds-provider-CmKbYAvr.js → rds-provider-ByCpdpUq.js} +208 -42
  19. package/dist/rds-provider-ByCpdpUq.js.map +1 -0
  20. package/dist/secret-redaction-BHWS-PZK.js +3937 -0
  21. package/dist/secret-redaction-BHWS-PZK.js.map +1 -0
  22. package/dist/{version-CaejipV3.js → version-DDpKuzTw.js} +2 -2
  23. package/dist/{version-CaejipV3.js.map → version-DDpKuzTw.js.map} +1 -1
  24. package/package.json +2 -2
  25. package/dist/asg-provider-BpRcm_UB.js.map +0 -1
  26. package/dist/deploy-engine-CYVt2NQR.js.map +0 -1
  27. package/dist/iam-role-provider-R1GC6KcT.js.map +0 -1
  28. package/dist/neptune-provider-DzTl6-74.js.map +0 -1
  29. package/dist/program-CvRs5pV_.js.map +0 -1
  30. package/dist/provider-classes-B2_u4Fg0.js.map +0 -1
  31. package/dist/rds-provider-CmKbYAvr.js.map +0 -1
@@ -0,0 +1,3937 @@
1
+ import { Q as safeMsg, z as SAFE_MSG_ALTERED_CHAR } from "./aws-failure-text-e_ddrDDo.js";
2
+
3
+ //#region src/deployment/secret-redaction/rules.ts
4
+ const MIN_NEEDLE_LENGTH = 4;
5
+ function printingNeedles(values) {
6
+ const out = /* @__PURE__ */ new Set();
7
+ for (const value of values) {
8
+ if (value.length < 4) continue;
9
+ out.add(value);
10
+ const flat = flattenedSpelling(value);
11
+ if (flat !== void 0 && flat.trim().length >= 4) out.add(flat);
12
+ }
13
+ return [...out];
14
+ }
15
+ const FLATTENED_MEMO_CAP = 4096;
16
+ const flattenedMemo = /* @__PURE__ */ new Map();
17
+ function flattenedSpelling(value) {
18
+ if (!SAFE_MSG_ALTERED_CHAR.test(value)) return void 0;
19
+ let flat = flattenedMemo.get(value);
20
+ if (flat === void 0) {
21
+ flat = safeMsg`${value}`;
22
+ if (flattenedMemo.size >= FLATTENED_MEMO_CAP) flattenedMemo.clear();
23
+ flattenedMemo.set(value, flat);
24
+ }
25
+ return flat === value ? void 0 : flat;
26
+ }
27
+ function escapeRegExp(value) {
28
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
29
+ }
30
+ function maskMatchSpans(text, needles, mask) {
31
+ const spans = [];
32
+ for (const needle of needles) for (let at = text.indexOf(needle); at !== -1; at = text.indexOf(needle, at + 1)) spans.push([at, at + needle.length]);
33
+ if (spans.length === 0) return text;
34
+ spans.sort((a, b) => a[0] - b[0] || b[1] - a[1]);
35
+ let out = "";
36
+ let cursor = 0;
37
+ let [start, end] = spans[0];
38
+ for (const [nextStart, nextEnd] of spans.slice(1)) {
39
+ if (nextStart < end) {
40
+ end = Math.max(end, nextEnd);
41
+ continue;
42
+ }
43
+ out += text.slice(cursor, start) + mask;
44
+ cursor = end;
45
+ [start, end] = [nextStart, nextEnd];
46
+ }
47
+ return out + text.slice(cursor, start) + mask + text.slice(end);
48
+ }
49
+ function buildNeedleRegex(values) {
50
+ const needles = Array.from(new Set(values)).filter((v) => v.length >= 4).sort((a, b) => b.length - a.length);
51
+ if (needles.length === 0) return void 0;
52
+ return new RegExp(needles.map(escapeRegExp).join("|"), "g");
53
+ }
54
+ const TEMPLATE_DERIVED_RULES = {
55
+ descendArrays: true,
56
+ trustAnyExpression: false,
57
+ sourceIsSameGeneration: false
58
+ };
59
+ const TEMPLATE_SOURCED_RULES = {
60
+ descendArrays: false,
61
+ trustAnyExpression: false,
62
+ sourceIsSameGeneration: false
63
+ };
64
+ const STATE_SOURCED_READBACK_RULES = {
65
+ descendArrays: false,
66
+ trustAnyExpression: true,
67
+ sourceIsSameGeneration: true
68
+ };
69
+ const STATE_SOURCED_BASELINE_RULES = {
70
+ descendArrays: false,
71
+ trustAnyExpression: true,
72
+ sourceIsSameGeneration: true,
73
+ failClosedOnUncertifiedPositions: true
74
+ };
75
+ const STATE_SOURCED_CROSS_GENERATION_RULES = {
76
+ descendArrays: false,
77
+ trustAnyExpression: true,
78
+ sourceIsSameGeneration: false
79
+ };
80
+ const STATE_DERIVED_RULES = {
81
+ descendArrays: true,
82
+ trustAnyExpression: true,
83
+ sourceIsSameGeneration: true
84
+ };
85
+ function isDynamicReferenceString(value) {
86
+ return typeof value === "string" && value.includes("{{resolve:");
87
+ }
88
+ const DYNAMIC_REFERENCE_INNER_CHAR = "[^}]";
89
+ const DYNAMIC_REFERENCE_INNER = `${DYNAMIC_REFERENCE_INNER_CHAR}+`;
90
+ const WHOLE_DYNAMIC_REFERENCE_PATTERN = new RegExp(`^\\{\\{resolve:${DYNAMIC_REFERENCE_INNER}\\}\\}$`);
91
+ function isSingleDynamicReferenceToken(value) {
92
+ return WHOLE_DYNAMIC_REFERENCE_PATTERN.test(value);
93
+ }
94
+ function isPlainObject(value) {
95
+ return value !== null && typeof value === "object" && !Array.isArray(value);
96
+ }
97
+
98
+ //#endregion
99
+ //#region src/deployment/secret-redaction/mask-only.ts
100
+ const recordedSecretExpressions = /* @__PURE__ */ new Set();
101
+ const secretVerdictScopes = /* @__PURE__ */ new Map();
102
+ function pinScopedSecretVerdict(scope, expression, secret) {
103
+ const scopes = secretVerdictScopes.get(expression);
104
+ if (secret) {
105
+ if (scopes) scopes.add(scope);
106
+ else secretVerdictScopes.set(expression, /* @__PURE__ */ new Set([scope]));
107
+ recordedSecretExpressions.add(expression);
108
+ return;
109
+ }
110
+ if (scopes?.delete(scope) && scopes.size === 0) secretVerdictScopes.delete(expression);
111
+ recordedSecretExpressions.delete(expression);
112
+ }
113
+ function isScopedSecretVerdict(scope, expression) {
114
+ return secretVerdictScopes.get(expression)?.has(scope) === true;
115
+ }
116
+ function isRecordedSecretExpression(expression) {
117
+ return recordedSecretExpressions.has(expression);
118
+ }
119
+ const provenPublicExpressions = /* @__PURE__ */ new WeakMap();
120
+ function proofsOf(bag) {
121
+ let proofs = provenPublicExpressions.get(bag);
122
+ if (!proofs) {
123
+ proofs = /* @__PURE__ */ new Map();
124
+ provenPublicExpressions.set(bag, proofs);
125
+ }
126
+ return proofs;
127
+ }
128
+ function recordProvenPublicExpression(bag, expression, value) {
129
+ const proofs = proofsOf(bag);
130
+ const prior = proofs.get(expression);
131
+ if (prior === false) return;
132
+ proofs.set(expression, prior === void 0 || prior === value ? value : false);
133
+ }
134
+ function sharePublicProofs(view, pass) {
135
+ provenPublicExpressions.set(view, proofsOf(pass));
136
+ }
137
+ function carryPublicProofs(from, to) {
138
+ for (const [expression, value] of provenPublicExpressions.get(from) ?? []) if (value === false) contradictProvenPublicExpression(to, expression);
139
+ else recordProvenPublicExpression(to, expression, value);
140
+ }
141
+ function contradictProvenPublicExpression(bag, expression) {
142
+ proofsOf(bag).set(expression, false);
143
+ }
144
+ function provenPublicValue(bag, expression) {
145
+ const value = provenPublicExpressions.get(bag)?.get(expression);
146
+ if (value === void 0 || value === false) return void 0;
147
+ if (recordedSecretExpressions.has(expression)) return void 0;
148
+ for (const recorded of bag.values()) if (recorded === expression) return void 0;
149
+ return value;
150
+ }
151
+ function recordMaskOnlyValue(secrets, plaintext) {
152
+ if (plaintext.length < 4) return;
153
+ const existing = secrets.get(plaintext);
154
+ if (existing !== void 0 && existing !== "***") return;
155
+ secrets.set(plaintext, "***");
156
+ }
157
+ function isMaskOnlyPlaintext(secrets, plaintext) {
158
+ return secrets.get(plaintext) === "***";
159
+ }
160
+ function recordMaskOnlyValuesIn(value, secrets, excluded) {
161
+ const seen = /* @__PURE__ */ new Set();
162
+ const walk = (node) => {
163
+ if (typeof node === "string") {
164
+ if (excluded?.has(node) === true) return;
165
+ recordMaskOnlyValue(secrets, node);
166
+ return;
167
+ }
168
+ if (node === null || typeof node !== "object") return;
169
+ if (seen.has(node)) return;
170
+ seen.add(node);
171
+ if (Array.isArray(node)) {
172
+ for (const item of node) walk(item);
173
+ return;
174
+ }
175
+ for (const child of Object.values(node)) walk(child);
176
+ };
177
+ walk(value);
178
+ }
179
+ function wholeStringLeavesOf(value) {
180
+ const leaves = /* @__PURE__ */ new Set();
181
+ const seen = /* @__PURE__ */ new Set();
182
+ const walk = (node) => {
183
+ if (typeof node === "string") {
184
+ leaves.add(node);
185
+ return;
186
+ }
187
+ if (node === null || typeof node !== "object") return;
188
+ if (seen.has(node)) return;
189
+ seen.add(node);
190
+ if (Array.isArray(node)) {
191
+ for (const item of node) walk(item);
192
+ return;
193
+ }
194
+ for (const child of Object.values(node)) walk(child);
195
+ };
196
+ walk(value);
197
+ return leaves;
198
+ }
199
+ function recordNoEchoAttributeValues(result, secrets, ownProperties) {
200
+ const attributes = result.attributes;
201
+ if (attributes === void 0) return void 0;
202
+ const excluded = ownProperties === void 0 ? void 0 : wholeStringLeavesOf(ownProperties);
203
+ if (result.noEchoAttributes === true) {
204
+ recordMaskOnlyValuesIn(attributes, secrets, excluded);
205
+ return true;
206
+ }
207
+ const names = (result.noEchoAttributeNames ?? []).filter((name) => Object.hasOwn(attributes, name));
208
+ if (names.length === 0) return void 0;
209
+ for (const name of names) recordMaskOnlyValuesIn(attributes[name], secrets, excluded);
210
+ return new Set(names);
211
+ }
212
+ function carriesSecretMask(value) {
213
+ const seen = /* @__PURE__ */ new Set();
214
+ const walk = (node) => {
215
+ if (typeof node === "string") return node === "***";
216
+ if (node === null || typeof node !== "object") return false;
217
+ if (seen.has(node)) return false;
218
+ seen.add(node);
219
+ if (Array.isArray(node)) return node.some((item) => walk(item));
220
+ return Object.values(node).some((child) => walk(child));
221
+ };
222
+ return walk(value);
223
+ }
224
+ const freshNoEchoValuesOf = /* @__PURE__ */ new WeakMap();
225
+ function freshNoEchoSet(secrets) {
226
+ return sideSetOf(freshNoEchoValuesOf, secrets);
227
+ }
228
+ const containmentValuesOf = /* @__PURE__ */ new WeakMap();
229
+ function sideSetOf(table, secrets) {
230
+ let set = table.get(secrets);
231
+ if (set === void 0) {
232
+ set = /* @__PURE__ */ new Set();
233
+ table.set(secrets, set);
234
+ }
235
+ return set;
236
+ }
237
+ function recordDerivedMaskOnlyValue(secrets, plaintext) {
238
+ recordMaskOnlyValue(secrets, plaintext);
239
+ if (isMaskOnlyPlaintext(secrets, plaintext)) sideSetOf(containmentValuesOf, secrets).add(plaintext);
240
+ }
241
+ function recordFreshNoEchoValuesIn(value, secrets, excluded, publicTokens) {
242
+ recordMaskOnlyValuesIn(value, secrets, excluded);
243
+ const fresh = freshNoEchoSet(secrets);
244
+ const containment = sideSetOf(containmentValuesOf, secrets);
245
+ const seen = /* @__PURE__ */ new Set();
246
+ const walk = (node) => {
247
+ if (typeof node === "string") {
248
+ if (excluded?.has(node) !== true && isMaskOnlyPlaintext(secrets, node)) {
249
+ fresh.add(node);
250
+ if (publicTokens?.has(node) !== true) containment.add(node);
251
+ }
252
+ return;
253
+ }
254
+ if (node === null || typeof node !== "object") return;
255
+ if (seen.has(node)) return;
256
+ seen.add(node);
257
+ if (Array.isArray(node)) {
258
+ for (const item of node) walk(item);
259
+ return;
260
+ }
261
+ for (const child of Object.values(node)) walk(child);
262
+ };
263
+ walk(value);
264
+ }
265
+ const noEchoParameterValuesOf = /* @__PURE__ */ new WeakMap();
266
+ function recordNoEchoParameterFreshValue(value, secrets, publicTokens) {
267
+ recordFreshNoEchoValuesIn(value, secrets, void 0, publicTokens);
268
+ const parameterClass = sideSetOf(noEchoParameterValuesOf, secrets);
269
+ const walk = (node) => {
270
+ if (typeof node === "string") {
271
+ if (isMaskOnlyPlaintext(secrets, node)) parameterClass.add(node);
272
+ return;
273
+ }
274
+ if (Array.isArray(node)) for (const item of node) walk(item);
275
+ };
276
+ walk(value);
277
+ }
278
+ function withoutNoEchoParameterEntries(secrets) {
279
+ const fresh = freshNoEchoValuesOf.get(secrets);
280
+ if (fresh === void 0 || fresh.size === 0) return secrets;
281
+ const copy = /* @__PURE__ */ new Map();
282
+ for (const [plaintext, expression] of secrets) {
283
+ if (expression === "***" && fresh.has(plaintext)) continue;
284
+ copy.set(plaintext, expression);
285
+ }
286
+ mergeResolvedPairs(secrets, copy);
287
+ carryMaskOnlyMarks(secrets, copy);
288
+ return copy;
289
+ }
290
+ function markNoEchoParameterClass(secrets, plaintext) {
291
+ if (isMaskOnlyPlaintext(secrets, plaintext)) sideSetOf(noEchoParameterValuesOf, secrets).add(plaintext);
292
+ }
293
+ function isNoEchoParameterPlaintext(secrets, plaintext) {
294
+ return noEchoParameterValuesOf.get(secrets)?.has(plaintext) === true && isMaskOnlyPlaintext(secrets, plaintext);
295
+ }
296
+ function noEchoParameterPlaintextsOf(secrets) {
297
+ return [...noEchoParameterValuesOf.get(secrets) ?? []].filter((plaintext) => isMaskOnlyPlaintext(secrets, plaintext));
298
+ }
299
+ function carryFreshNoEchoMark(from, to, plaintext) {
300
+ if (!isMaskOnlyPlaintext(to, plaintext)) return;
301
+ if (containmentValuesOf.get(from)?.has(plaintext) === true) sideSetOf(containmentValuesOf, to).add(plaintext);
302
+ if (freshNoEchoValuesOf.get(from)?.has(plaintext) === true) freshNoEchoSet(to).add(plaintext);
303
+ if (noEchoParameterValuesOf.get(from)?.has(plaintext) === true) sideSetOf(noEchoParameterValuesOf, to).add(plaintext);
304
+ }
305
+ function carryMaskOnlyMarks(from, to) {
306
+ for (const table of [
307
+ freshNoEchoValuesOf,
308
+ containmentValuesOf,
309
+ noEchoParameterValuesOf
310
+ ]) {
311
+ const marks = table.get(from);
312
+ if (marks === void 0) continue;
313
+ for (const plaintext of marks) if (isMaskOnlyPlaintext(to, plaintext)) sideSetOf(table, to).add(plaintext);
314
+ }
315
+ }
316
+ function isKnownSecretExpression(expression, secretExpressions) {
317
+ return isSecretExpressionByVerdictOrSpelling(expression) || secretExpressions.has(expression);
318
+ }
319
+ function isSecretExpressionByVerdictOrSpelling(expression) {
320
+ return expression.startsWith("{{resolve:secretsmanager:") || expression.startsWith("{{resolve:ssm-secure:") || isRecordedSecretExpression(expression);
321
+ }
322
+
323
+ //#endregion
324
+ //#region src/deployment/secret-redaction/cross-stack.ts
325
+ const crossStackAssociations = /* @__PURE__ */ new WeakMap();
326
+ const CONFLICTING_CROSS_STACK = Symbol("conflicting cross-stack association");
327
+ const CROSS_STACK_KEY_SEPARATOR = "\0";
328
+ function literalStringOrUndefined(value) {
329
+ return typeof value === "string" && value !== "" ? value : void 0;
330
+ }
331
+ function splitGetAttStringForm(getAtt) {
332
+ const firstDot = getAtt.indexOf(".");
333
+ if (firstDot <= 0 || firstDot === getAtt.length - 1) return void 0;
334
+ return {
335
+ logicalId: getAtt.slice(0, firstDot),
336
+ attributeName: getAtt.slice(firstDot + 1)
337
+ };
338
+ }
339
+ function crossStackSourceKey(source) {
340
+ const keys = Object.keys(source);
341
+ if (keys.length !== 1) return void 0;
342
+ const key = keys[0];
343
+ if (key === "Fn::ImportValue") {
344
+ const exportName = literalStringOrUndefined(source[key]);
345
+ if (exportName === void 0) return void 0;
346
+ return ["Fn::ImportValue", exportName].join(CROSS_STACK_KEY_SEPARATOR);
347
+ }
348
+ if (key === "Fn::GetStackOutput") {
349
+ const args = source[key];
350
+ if (!isPlainObject(args)) return void 0;
351
+ const stackName = Object.hasOwn(args, "StackName") ? literalStringOrUndefined(args["StackName"]) : void 0;
352
+ const outputName = Object.hasOwn(args, "OutputName") ? literalStringOrUndefined(args["OutputName"]) : void 0;
353
+ if (stackName === void 0 || outputName === void 0) return void 0;
354
+ const slots = [
355
+ "Fn::GetStackOutput",
356
+ stackName,
357
+ outputName
358
+ ];
359
+ for (const optional of ["Region", "RoleArn"]) {
360
+ const raw = Object.hasOwn(args, optional) ? args[optional] : void 0;
361
+ if (raw === void 0 || raw === null) {
362
+ slots.push("");
363
+ continue;
364
+ }
365
+ const literal = literalStringOrUndefined(raw);
366
+ if (literal === void 0) return void 0;
367
+ slots.push(literal);
368
+ }
369
+ return slots.join(CROSS_STACK_KEY_SEPARATOR);
370
+ }
371
+ if (key === "Fn::GetAtt") {
372
+ const raw = source[key];
373
+ let logicalId;
374
+ let attributeName;
375
+ if (typeof raw === "string") {
376
+ const split = splitGetAttStringForm(raw);
377
+ if (split === void 0) return void 0;
378
+ logicalId = split.logicalId;
379
+ attributeName = split.attributeName;
380
+ } else if (Array.isArray(raw) && raw.length === 2) {
381
+ logicalId = literalStringOrUndefined(raw[0]);
382
+ attributeName = literalStringOrUndefined(raw[1]);
383
+ }
384
+ if (logicalId === void 0 || attributeName === void 0) return void 0;
385
+ return [
386
+ "Fn::GetAtt",
387
+ logicalId,
388
+ attributeName
389
+ ].join(CROSS_STACK_KEY_SEPARATOR);
390
+ }
391
+ if (key === "Fn::Sub") {
392
+ const raw = source[key];
393
+ let template;
394
+ let variables;
395
+ if (typeof raw === "string") template = raw;
396
+ else if (Array.isArray(raw) && raw.length === 2 && isPlainObject(raw[1])) {
397
+ if (typeof raw[0] !== "string") return void 0;
398
+ template = raw[0];
399
+ variables = raw[1];
400
+ }
401
+ if (template === void 0) return void 0;
402
+ const only = /^\$\{(!)?([^}]*)\}$/.exec(template);
403
+ if (only === null || only[1] === "!") return void 0;
404
+ const varName = only[2] ?? "";
405
+ if (varName in (variables ?? {})) return void 0;
406
+ const split = splitGetAttStringForm(varName);
407
+ if (split === void 0) return void 0;
408
+ return [
409
+ "Fn::GetAtt",
410
+ split.logicalId,
411
+ split.attributeName
412
+ ].join(CROSS_STACK_KEY_SEPARATOR);
413
+ }
414
+ if (key === "Ref") {
415
+ const parameterName = literalStringOrUndefined(source[key]);
416
+ if (parameterName === void 0) return void 0;
417
+ return ["Ref", parameterName].join(CROSS_STACK_KEY_SEPARATOR);
418
+ }
419
+ }
420
+ function recordCrossStackExpression(secrets, key, expression, plaintext) {
421
+ if (!isSingleDynamicReferenceToken(expression)) return;
422
+ let associations = crossStackAssociations.get(secrets);
423
+ if (associations === void 0) {
424
+ associations = /* @__PURE__ */ new Map();
425
+ crossStackAssociations.set(secrets, associations);
426
+ }
427
+ storeAssociation(associations, key, expression, plaintext);
428
+ }
429
+ function storeAssociation(associations, key, expression, plaintext) {
430
+ const seen = associations.get(key);
431
+ if (seen === void 0) {
432
+ associations.set(key, {
433
+ expression,
434
+ plaintext
435
+ });
436
+ return;
437
+ }
438
+ if (typeof seen === "symbol") return;
439
+ if (seen.expression !== expression || seen.plaintext !== plaintext) associations.set(key, CONFLICTING_CROSS_STACK);
440
+ }
441
+
442
+ //#endregion
443
+ //#region src/deployment/secret-redaction/certified-positions.ts
444
+ function plaintextIndexOf(secrets) {
445
+ const plaintextOf = /* @__PURE__ */ new Map();
446
+ for (const [plaintext, expression] of secrets) plaintextOf.set(expression, plaintextOf.has(expression) ? CONFLICTING_PLAINTEXT : plaintext);
447
+ return plaintextOf;
448
+ }
449
+ function certifiedExpressionForLeaf(secrets, association, leaf) {
450
+ if (typeof leaf !== "string" || leaf === "") return void 0;
451
+ if (!secrets.has(leaf)) return void 0;
452
+ if (association.plaintext !== leaf) return void 0;
453
+ const recordedPlaintext = plaintextIndexOf(secrets).get(association.expression);
454
+ if (recordedPlaintext !== void 0 && recordedPlaintext !== leaf) return void 0;
455
+ return association.expression;
456
+ }
457
+ function certifiedListForLeaf(secrets, association, bag) {
458
+ const certified = bag.map((element) => certifiedExpressionForLeaf(secrets, association, element));
459
+ if (certified.every((expression) => expression === void 0)) return void 0;
460
+ return bag.map((element, i) => certified[i] ?? redactForState(element, {
461
+ provenance: "value-scan",
462
+ secrets
463
+ }));
464
+ }
465
+ function associationForSource(source, secrets) {
466
+ const key = crossStackSourceKey(source);
467
+ if (key === void 0) return void 0;
468
+ const associations = crossStackAssociations.get(secrets);
469
+ if (associations === void 0) return void 0;
470
+ const association = associations.get(key);
471
+ if (association === void 0 || typeof association === "symbol") return void 0;
472
+ return association;
473
+ }
474
+ function positionByCrossStackSource(bag, source, secrets) {
475
+ const association = associationForSource(source, secrets);
476
+ if (association === void 0) return void 0;
477
+ return certifiedExpressionForLeaf(secrets, association, bag);
478
+ }
479
+
480
+ //#endregion
481
+ //#region src/deployment/secret-redaction/resolver-outcomes.ts
482
+ const CONFLICTING = Symbol("cdkd.resolver-outcome.conflicting");
483
+ const takenBranches = /* @__PURE__ */ new WeakMap();
484
+ function recordTakenIfBranch(secrets, source, tookTrueBranch) {
485
+ let taken = takenBranches.get(secrets);
486
+ if (taken === void 0) {
487
+ taken = /* @__PURE__ */ new WeakMap();
488
+ takenBranches.set(secrets, taken);
489
+ }
490
+ const previous = taken.get(source);
491
+ if (previous === void 0) taken.set(source, tookTrueBranch);
492
+ else if (previous !== tookTrueBranch) taken.set(source, CONFLICTING);
493
+ }
494
+ function takenIfBranchOf(secrets, source) {
495
+ const taken = takenBranches.get(secrets)?.get(source);
496
+ return taken === CONFLICTING ? void 0 : taken;
497
+ }
498
+ const droppedElements = /* @__PURE__ */ new WeakMap();
499
+ function recordDroppedArrayElements(secrets, source, dropped) {
500
+ let arrays = droppedElements.get(secrets);
501
+ if (arrays === void 0) {
502
+ arrays = /* @__PURE__ */ new WeakMap();
503
+ droppedElements.set(secrets, arrays);
504
+ }
505
+ const previous = arrays.get(source);
506
+ if (previous === void 0) arrays.set(source, [...dropped]);
507
+ else if (previous !== CONFLICTING && (previous.length !== dropped.length || previous.some((index, i) => index !== dropped[i]))) arrays.set(source, CONFLICTING);
508
+ }
509
+ function droppedArrayElementsOf(secrets, source) {
510
+ const dropped = droppedElements.get(secrets)?.get(source);
511
+ return dropped === CONFLICTING ? void 0 : dropped;
512
+ }
513
+
514
+ //#endregion
515
+ //#region src/deployment/secret-redaction/physical-name-copies.ts
516
+ const physicalNameCopiesOf = /* @__PURE__ */ new WeakMap();
517
+ function recordPhysicalNameCopy(secrets, physicalId) {
518
+ if (physicalId === "") return;
519
+ sideSetOf(physicalNameCopiesOf, secrets).add(physicalId);
520
+ }
521
+ const attributeIdCopiesOf = /* @__PURE__ */ new WeakMap();
522
+ function recordAttributeIdCopy(secrets, physicalId) {
523
+ if (physicalId === "") return;
524
+ sideSetOf(attributeIdCopiesOf, secrets).add(physicalId);
525
+ }
526
+ function physicalNameSpans(names, value) {
527
+ const spans = [];
528
+ if (names === void 0) return spans;
529
+ for (const name of names) for (let at = value.indexOf(name); at !== -1; at = value.indexOf(name, at + 1)) spans.push({
530
+ start: at,
531
+ end: at + name.length
532
+ });
533
+ return spans;
534
+ }
535
+
536
+ //#endregion
537
+ //#region src/deployment/secret-redaction/fresh-noecho.ts
538
+ function embedsFreshNoEchoValue(text, secrets, freshClass) {
539
+ const fresh = freshNoEchoValuesOf.get(secrets);
540
+ if (fresh === void 0) return false;
541
+ for (const value of fresh) {
542
+ if (freshClass !== void 0 && isNoEchoParameterPlaintext(secrets, value) !== (freshClass === "parameter")) continue;
543
+ if (isMaskOnlyPlaintext(secrets, value) && text.includes(value)) return true;
544
+ }
545
+ return false;
546
+ }
547
+ function carriesFreshNoEchoValue(value, secrets, freshClass) {
548
+ const isFresh = freshNoEchoLeafTest(secrets, freshClass);
549
+ if (isFresh === void 0) return false;
550
+ const seen = /* @__PURE__ */ new Set();
551
+ const walk = (node) => {
552
+ if (typeof node === "string") return isFresh(node);
553
+ if (node === null || typeof node !== "object") return false;
554
+ if (seen.has(node)) return false;
555
+ seen.add(node);
556
+ if (Array.isArray(node)) return node.some((item) => walk(item));
557
+ return Object.values(node).some((child) => walk(child));
558
+ };
559
+ return walk(value);
560
+ }
561
+ function freshNoEchoLeafPositions(value, secrets, freshClass) {
562
+ const isFresh = freshNoEchoLeafTest(secrets, freshClass);
563
+ if (isFresh === void 0) return [];
564
+ const leaves = [];
565
+ const ancestors = /* @__PURE__ */ new Set();
566
+ const walk = (node, path) => {
567
+ if (typeof node === "string") {
568
+ if (isFresh(node)) leaves.push({
569
+ path,
570
+ plaintext: node
571
+ });
572
+ return;
573
+ }
574
+ if (node === null || typeof node !== "object") return;
575
+ if (ancestors.has(node)) return;
576
+ ancestors.add(node);
577
+ if (Array.isArray(node)) node.forEach((item, index) => walk(item, [...path, index]));
578
+ else for (const [key, child] of Object.entries(node)) walk(child, [...path, key]);
579
+ ancestors.delete(node);
580
+ };
581
+ walk(value, []);
582
+ return leaves;
583
+ }
584
+ function freshNoEchoLeafTest(secrets, freshClass) {
585
+ const all = freshNoEchoValuesOf.get(secrets);
586
+ if (all === void 0 || all.size === 0) return void 0;
587
+ const fresh = freshClass === void 0 ? all : new Set([...all].filter((value) => isNoEchoParameterPlaintext(secrets, value) === (freshClass === "parameter")));
588
+ if (fresh.size === 0) return void 0;
589
+ const needles = containmentNeedlesOf(secrets).filter((needle) => fresh.has(needle));
590
+ return (leaf) => fresh.has(leaf) && isMaskOnlyPlaintext(secrets, leaf) || leaf !== "***" && embedsNeedleOutsideReferences(leaf, needles, secrets);
591
+ }
592
+ function containmentNeedlesOf(secrets) {
593
+ const values = containmentValuesOf.get(secrets);
594
+ if (values === void 0 || values.size === 0) return [];
595
+ const needles = [];
596
+ for (const value of values) if (value.length >= 4 && isMaskOnlyPlaintext(secrets, value)) needles.push(value);
597
+ return needles;
598
+ }
599
+ function isStrictlyInsideASpan(spans, start, end) {
600
+ return spans.some((span) => span.start <= start && end <= span.end && (span.start !== start || span.end !== end));
601
+ }
602
+ function embedsNeedleOutsideReferences(leaf, needles, secrets) {
603
+ return needleOccurrencesOutsideReferences(leaf, needles, secrets, true).length > 0;
604
+ }
605
+ function needleOccurrencesOutsideReferences(leaf, needles, secrets, first) {
606
+ const found = [];
607
+ let spans;
608
+ let names;
609
+ for (const needle of needles) for (let at = leaf.indexOf(needle); at >= 0; at = leaf.indexOf(needle, at + 1)) {
610
+ spans ??= isDynamicReferenceString(leaf) ? resolvableReferenceSpans(leaf, new Set(secrets.values())) : [];
611
+ if (isStrictlyInsideASpan(spans, at, at + needle.length)) continue;
612
+ names ??= physicalNameSpans(physicalNameCopiesOf.get(secrets), leaf);
613
+ const end = at + needle.length;
614
+ if (names.some((n) => n.start <= at && end <= n.end)) continue;
615
+ found.push({
616
+ start: at,
617
+ end
618
+ });
619
+ if (first) return found;
620
+ }
621
+ return found;
622
+ }
623
+ function cutsContainmentNeedle(leaf, secrets, redacted) {
624
+ if (redacted.length === 0) return false;
625
+ const needles = containmentNeedlesOf(secrets);
626
+ if (needles.length === 0) return false;
627
+ return needleOccurrencesOutsideReferences(leaf, needles, secrets, false).some((c) => redacted.some((m) => m.start < c.end && c.start < m.end && !(m.start <= c.start && c.end <= m.end)));
628
+ }
629
+ function flattenEmbeddedNoEchoLeaves(value, secrets) {
630
+ const needles = containmentNeedlesOf(secrets);
631
+ if (needles.length === 0) return value;
632
+ const flattens = (leaf) => leaf !== "***" && embedsNeedleOutsideReferences(leaf, needles, secrets);
633
+ const walk = (node) => {
634
+ if (typeof node === "string") return flattens(node) ? "***" : node;
635
+ if (node === null || typeof node !== "object") return node;
636
+ if (Array.isArray(node)) {
637
+ let changed = false;
638
+ const out = node.map((item) => {
639
+ const next = walk(item);
640
+ if (next !== item) changed = true;
641
+ return next;
642
+ });
643
+ return changed ? out : node;
644
+ }
645
+ if (isOrdinaryDate(node)) {
646
+ const persisted = node.toJSON();
647
+ return typeof persisted === "string" && flattens(persisted) ? "***" : node;
648
+ }
649
+ let out;
650
+ for (const [key, child] of Object.entries(node)) {
651
+ const next = walk(child);
652
+ if (next === child) continue;
653
+ out ??= Object.assign(Object.create(null), node);
654
+ out[key] = next;
655
+ }
656
+ return out ?? node;
657
+ };
658
+ return walk(value);
659
+ }
660
+ function substringNeedlesOf(secrets) {
661
+ const needles = [];
662
+ for (const plaintext of secrets.keys()) if (!isMaskOnlyPlaintext(secrets, plaintext)) needles.push(plaintext);
663
+ return needles;
664
+ }
665
+ function recordedExpressionsOf(secrets) {
666
+ const expressions = /* @__PURE__ */ new Set();
667
+ for (const [plaintext, expression] of secrets) if (!isMaskOnlyPlaintext(secrets, plaintext)) expressions.add(expression);
668
+ return expressions;
669
+ }
670
+ const recoverableMaskedOutputs = /* @__PURE__ */ new Map();
671
+ function maskedOutputKey(identity, stackName, region, outputKey) {
672
+ return `${identity}\u0000${stackName}\u0000${region}\u0000${outputKey}`;
673
+ }
674
+ function recordRecoverableMaskedOutput(identity, stackName, region, outputKey, plaintext) {
675
+ recoverableMaskedOutputs.set(maskedOutputKey(identity, stackName, region, outputKey), plaintext);
676
+ }
677
+ function recoverMaskedOutput(identity, stackName, region, outputKey) {
678
+ return recoverableMaskedOutputs.get(maskedOutputKey(identity, stackName, region, outputKey));
679
+ }
680
+
681
+ //#endregion
682
+ //#region src/deployment/secret-redaction/nested-stack.ts
683
+ const NESTED_STACK_RESOURCE_TYPE = "AWS::CloudFormation::Stack";
684
+ const nestedStackParameterExpressions = /* @__PURE__ */ new WeakMap();
685
+ const renderedParameterSpellings = /* @__PURE__ */ new WeakMap();
686
+ const UNFRAMED_SPELLING = Symbol("cdkd.nested-parameter.unframed-spelling");
687
+ function recordNestedStackParameterExpressions(secrets, resourceType, resolvedProperties, sourceProperties, provenance = "template-derived") {
688
+ if (resourceType !== NESTED_STACK_RESOURCE_TYPE) return;
689
+ if (secrets.size === 0) return;
690
+ if (!isPlainObject(resolvedProperties) || !isPlainObject(sourceProperties)) return;
691
+ if (!Object.hasOwn(resolvedProperties, "Parameters")) return;
692
+ if (!Object.hasOwn(sourceProperties, "Parameters")) return;
693
+ const resolvedParameters = resolvedProperties["Parameters"];
694
+ const sourceParameters = sourceProperties["Parameters"];
695
+ if (!isPlainObject(resolvedParameters) || !isPlainObject(sourceParameters)) return;
696
+ const positioned = redactAtProvenance({ ...resolvedParameters }, secrets, sourceParameters, provenance, true);
697
+ let table = nestedStackParameterExpressions.get(secrets);
698
+ for (const [name, resolvedValue] of Object.entries(resolvedParameters)) {
699
+ const sourceLeaf = sourceParameters[name];
700
+ if (typeof resolvedValue !== "string" || typeof sourceLeaf !== "string") continue;
701
+ if (isSingleDynamicReferenceToken(sourceLeaf) || positioned[name] !== sourceLeaf) continue;
702
+ if (!rendersLiteralTo(sourceLeaf, secrets, resolvedValue)) continue;
703
+ let rendered = renderedParameterSpellings.get(secrets);
704
+ if (rendered === void 0) {
705
+ rendered = /* @__PURE__ */ new Map();
706
+ renderedParameterSpellings.set(secrets, rendered);
707
+ }
708
+ const previous = rendered.get(name);
709
+ if (previous === void 0) rendered.set(name, {
710
+ spelling: sourceLeaf,
711
+ value: resolvedValue
712
+ });
713
+ else if (previous?.spelling !== sourceLeaf || previous.value !== resolvedValue) rendered.set(name, null);
714
+ }
715
+ for (const [name, resolvedValue] of Object.entries(resolvedParameters)) {
716
+ if (typeof resolvedValue !== "string" || !secrets.has(resolvedValue)) continue;
717
+ const expression = positioned[name];
718
+ if (typeof expression !== "string") continue;
719
+ if (!isSingleDynamicReferenceToken(expression)) continue;
720
+ const sourceLeaf = sourceParameters[name];
721
+ if (typeof sourceLeaf === "string" && expression !== sourceLeaf) continue;
722
+ if (expression === resolvedValue) continue;
723
+ const seenResolvingTo = plaintextIndexOf(secrets).get(expression);
724
+ if (seenResolvingTo !== void 0 && seenResolvingTo !== resolvedValue) continue;
725
+ if (typeof sourceLeaf === "string" && resolvedPlaintextOf(secrets, expression) !== resolvedValue) continue;
726
+ if (table === void 0) {
727
+ table = /* @__PURE__ */ new Map();
728
+ nestedStackParameterExpressions.set(secrets, table);
729
+ }
730
+ storeAssociation(table, name, expression, resolvedValue);
731
+ }
732
+ const framesByValue = /* @__PURE__ */ new Map();
733
+ const tokensOf = /* @__PURE__ */ new Map();
734
+ const frameSpellings = /* @__PURE__ */ new Map();
735
+ const frameSpellingOf = (name) => {
736
+ if (!frameSpellings.has(name)) frameSpellings.set(name, computeFrameSpelling(name));
737
+ return frameSpellings.get(name);
738
+ };
739
+ const computeFrameSpelling = (name) => {
740
+ const resolvedValue = resolvedParameters[name];
741
+ if (typeof resolvedValue !== "string") return void 0;
742
+ const sourceLeaf = sourceParameters[name];
743
+ if (typeof sourceLeaf === "string") return sourceLeaf;
744
+ if (!isPlainObject(sourceLeaf)) return void 0;
745
+ const written = positioned[name];
746
+ if (typeof written !== "string") return void 0;
747
+ const rendered = intrinsicSkeletonSegments(sourceLeaf)?.map((s) => s === UNKNOWN_PART ? "\0" : s).join("");
748
+ const spans = rendered === void 0 ? [] : dynamicReferenceSpans(rendered);
749
+ if (rendered !== void 0 && spans.length === 1) {
750
+ const [span] = spans;
751
+ const ownToken = rendered.slice(span.start, span.end);
752
+ if (written !== resolvedValue && SPELLED_SECRET_REFERENCE_PREFIXES.some((prefix) => ownToken.startsWith(prefix))) return written;
753
+ }
754
+ const substituted = substitutedSpellingOf(secrets, sourceLeaf, resolvedValue);
755
+ if (substituted === void 0) return void 0;
756
+ return written === resolvedValue || written === substituted ? substituted : void 0;
757
+ };
758
+ const certifiedSpellingOf = (name) => {
759
+ const sourceLeaf = sourceParameters[name];
760
+ if (typeof sourceLeaf === "string") return positioned[name] === sourceLeaf ? sourceLeaf : void 0;
761
+ return frameSpellingOf(name);
762
+ };
763
+ const unwrittenValues = /* @__PURE__ */ new Set();
764
+ for (const [name, resolvedValue] of Object.entries(resolvedParameters)) {
765
+ if (typeof resolvedValue !== "string") continue;
766
+ const spelling = frameSpellingOf(name);
767
+ const frame = spelling === void 0 ? void 0 : singleSpanFrame(resolvedValue, spelling);
768
+ const frames = framesByValue.get(resolvedValue) ?? /* @__PURE__ */ new Set();
769
+ const unframed = frame === void 0 || resolvedPlaintextOf(secrets, frame.token) !== frame.middle;
770
+ frames.add(unframed ? UNFRAMED_SPELLING : `${frame.prefix.length}:${frame.prefix}${frame.suffix}`);
771
+ framesByValue.set(resolvedValue, frames);
772
+ if (!unframed) {
773
+ const tokens = tokensOf.get(resolvedValue) ?? /* @__PURE__ */ new Set();
774
+ tokens.add(frame.token);
775
+ tokensOf.set(resolvedValue, tokens);
776
+ if (typeof sourceParameters[name] !== "string" && positioned[name] !== spelling) unwrittenValues.add(resolvedValue);
777
+ }
778
+ }
779
+ for (const value of unwrittenValues) if (tokensOf.get(value)?.size !== 1) framesByValue.get(value)?.add(UNFRAMED_SPELLING);
780
+ const rowWithoutParameters = {};
781
+ for (const [key, value] of Object.entries(resolvedProperties)) if (key !== "Parameters") rowWithoutParameters[key] = value;
782
+ const outsideLeaves = wholeStringLeavesOf(rowWithoutParameters);
783
+ const framed = /* @__PURE__ */ new Map();
784
+ const framedByName = [];
785
+ for (const [name, resolvedValue] of Object.entries(resolvedParameters)) {
786
+ if (typeof resolvedValue !== "string") continue;
787
+ const spelling = certifiedSpellingOf(name);
788
+ if (spelling === void 0) continue;
789
+ if (redactForState(resolvedValue, {
790
+ provenance: "value-scan",
791
+ secrets
792
+ }) !== resolvedValue) continue;
793
+ const frame = singleSpanFrame(resolvedValue, spelling);
794
+ if (frame === void 0 || resolvedPlaintextOf(secrets, frame.token) !== frame.middle) continue;
795
+ if (framesByValue.get(resolvedValue)?.size !== 1) continue;
796
+ const isNeedle = resolvedValue.length >= 4;
797
+ const rewrites = (leaf) => leaf === resolvedValue || isNeedle && leaf.includes(resolvedValue);
798
+ let embedded = false;
799
+ for (const leaf of outsideLeaves) if (rewrites(leaf)) {
800
+ embedded = true;
801
+ break;
802
+ }
803
+ for (const other of Object.values(resolvedParameters)) {
804
+ if (embedded) break;
805
+ if (typeof other === "string") {
806
+ if (other !== resolvedValue && rewrites(other)) embedded = true;
807
+ continue;
808
+ }
809
+ for (const leaf of wholeStringLeavesOf(other)) if (rewrites(leaf)) {
810
+ embedded = true;
811
+ break;
812
+ }
813
+ }
814
+ if (embedded) continue;
815
+ framedByName.push([
816
+ name,
817
+ spelling,
818
+ resolvedValue
819
+ ]);
820
+ if (secrets.get(frame.middle) !== frame.token && tokensOf.get(resolvedValue)?.size !== 1) continue;
821
+ framed.set(resolvedValue, spelling);
822
+ }
823
+ for (const [plaintext, expression] of framed) secrets.set(plaintext, expression);
824
+ for (const [name, expression, plaintext] of framedByName) {
825
+ if (table === void 0) {
826
+ table = /* @__PURE__ */ new Map();
827
+ nestedStackParameterExpressions.set(secrets, table);
828
+ }
829
+ storeAssociation(table, name, expression, plaintext);
830
+ }
831
+ }
832
+ function inheritNestedStackParameterAssociations(childSecrets, parentSecrets) {
833
+ const table = nestedStackParameterExpressions.get(parentSecrets);
834
+ if (table === void 0 || table.size === 0) return;
835
+ let associations = crossStackAssociations.get(childSecrets);
836
+ for (const [name, association] of table) {
837
+ const key = crossStackSourceKey({ Ref: name });
838
+ if (key === void 0) continue;
839
+ if (associations === void 0) {
840
+ associations = /* @__PURE__ */ new Map();
841
+ crossStackAssociations.set(childSecrets, associations);
842
+ }
843
+ if (typeof association === "symbol") {
844
+ associations.set(key, association);
845
+ continue;
846
+ }
847
+ storeAssociation(associations, key, association.expression, association.plaintext);
848
+ }
849
+ }
850
+ function inheritedParameterExpression(parentSecrets, parameterName, resolvedValue) {
851
+ const association = nestedStackParameterExpressions.get(parentSecrets)?.get(parameterName);
852
+ if (association === void 0) return renderedParameterSpelling(parentSecrets, parameterName, resolvedValue);
853
+ if (typeof association === "symbol") return void 0;
854
+ if (Array.isArray(resolvedValue)) return certifiedListForLeaf(parentSecrets, association, resolvedValue);
855
+ return certifiedExpressionForLeaf(parentSecrets, association, resolvedValue);
856
+ }
857
+ function renderedParameterSpelling(parentSecrets, parameterName, resolvedValue) {
858
+ if (typeof resolvedValue !== "string" || parentSecrets.has(resolvedValue)) return void 0;
859
+ const rendered = renderedParameterSpellings.get(parentSecrets)?.get(parameterName);
860
+ if (rendered == null || rendered.value !== resolvedValue) return void 0;
861
+ if (!rendersLiteralTo(rendered.spelling, parentSecrets, resolvedValue)) return void 0;
862
+ return rendered.spelling;
863
+ }
864
+ function inheritedRenderedToken(parentSecrets, parameterName, value, plaintext) {
865
+ const spelling = inheritedParameterExpression(parentSecrets, parameterName, value);
866
+ if (typeof spelling !== "string" || isSingleDynamicReferenceToken(spelling)) return void 0;
867
+ return renderedTokenOf(spelling, parentSecrets, plaintext);
868
+ }
869
+ function withdrawRenderedParameterSpelling(parentSecrets, parameterName) {
870
+ const rendered = renderedParameterSpellings.get(parentSecrets);
871
+ if (rendered?.has(parameterName)) rendered.set(parameterName, null);
872
+ }
873
+ function inheritedRenderedSpan(childSecrets, key, text) {
874
+ const reads = inheritedParameterReads.get(childSecrets);
875
+ if (reads === void 0 || typeof reads === "symbol") return void 0;
876
+ for (const name of reads.names) {
877
+ if (crossStackSourceKey({ Ref: name }) !== key) continue;
878
+ const value = renderedParameterSpellings.get(reads.parent)?.get(name)?.value;
879
+ if (value === void 0 || text !== void 0 && text !== value) return void 0;
880
+ if (carriesChildOnlyPlaintext(value, childSecrets, reads.parent)) return void 0;
881
+ const spelling = inheritedParameterExpression(reads.parent, name, value);
882
+ if (typeof spelling !== "string") return void 0;
883
+ if (redactForState(spelling, {
884
+ provenance: "value-scan",
885
+ secrets: childSecrets
886
+ }) !== spelling) return void 0;
887
+ return {
888
+ value,
889
+ spelling
890
+ };
891
+ }
892
+ }
893
+ const inheritedParameterReads = /* @__PURE__ */ new WeakMap();
894
+ const POISONED_PARAMETER_READS = Symbol("cdkd.inherited-parameter-reads.poisoned");
895
+ function parameterReadsFor(childSecrets, parentSecrets) {
896
+ const reads = inheritedParameterReads.get(childSecrets);
897
+ if (reads === void 0) {
898
+ const created = {
899
+ parent: parentSecrets,
900
+ names: /* @__PURE__ */ new Set(),
901
+ values: /* @__PURE__ */ new Map()
902
+ };
903
+ inheritedParameterReads.set(childSecrets, created);
904
+ return created;
905
+ }
906
+ if (typeof reads === "symbol") return void 0;
907
+ if (reads.parent !== parentSecrets) {
908
+ inheritedParameterReads.set(childSecrets, POISONED_PARAMETER_READS);
909
+ return;
910
+ }
911
+ return reads;
912
+ }
913
+ function recordInheritedParameterValue(childSecrets, parentSecrets, parameterName, value, declared) {
914
+ const reads = parameterReadsFor(childSecrets, parentSecrets);
915
+ if (reads === void 0) return;
916
+ if (declared !== void 0) {
917
+ if (reads.declared === void 0) reads.declared = declared;
918
+ else if (reads.declared.size !== declared.size || [...declared].some((name) => !reads.declared.has(name))) {
919
+ inheritedParameterReads.set(childSecrets, POISONED_PARAMETER_READS);
920
+ return;
921
+ }
922
+ }
923
+ if (reads.values.has(parameterName)) {
924
+ if (!deepEqualJsonValue(reads.values.get(parameterName), value)) inheritedParameterReads.set(childSecrets, POISONED_PARAMETER_READS);
925
+ return;
926
+ }
927
+ reads.values.set(parameterName, value);
928
+ }
929
+ function recordInheritedParameterRead(childSecrets, parentSecrets, parameterName) {
930
+ parameterReadsFor(childSecrets, parentSecrets)?.names.add(parameterName);
931
+ }
932
+ function redactInheritedParameterValue(parentSecrets, parameterName, value) {
933
+ return inheritedParameterExpression(parentSecrets, parameterName, value) ?? redactForState(value, {
934
+ provenance: "value-scan",
935
+ secrets: parentSecrets
936
+ });
937
+ }
938
+ function positionByInheritedParameter(bag, source, secrets) {
939
+ const keys = Object.keys(source);
940
+ if (keys.length !== 1 || keys[0] !== "Ref") return void 0;
941
+ const name = source["Ref"];
942
+ if (typeof name !== "string") return void 0;
943
+ const reads = inheritedParameterReads.get(secrets);
944
+ if (reads === void 0 || typeof reads === "symbol" || !reads.names.has(name)) return;
945
+ if (typeof bag !== "string" && !Array.isArray(bag)) return void 0;
946
+ if (carriesChildOnlyPlaintext(bag, secrets, reads.parent)) return void 0;
947
+ const answer = redactInheritedParameterValue(reads.parent, name, bag);
948
+ if (!deepEqualJsonValue(redactForState(answer, {
949
+ provenance: "value-scan",
950
+ secrets
951
+ }), answer)) return void 0;
952
+ return answer;
953
+ }
954
+ const REREAD_PSEUDO_PARAMETERS = /* @__PURE__ */ new Set([
955
+ "AWS::Region",
956
+ "AWS::Partition",
957
+ "AWS::URLSuffix",
958
+ "AWS::AccountId"
959
+ ]);
960
+ function templateRereadsAccountId(template) {
961
+ if (template === void 0) return false;
962
+ const resources = template.Resources !== null && typeof template.Resources === "object" ? template.Resources : void 0;
963
+ const cached = resources && accountRereadVerdicts.get(resources);
964
+ if (cached !== void 0 && cached.outputs === template.Outputs) return cached.verdict;
965
+ const parts = resources ? Object.values(resources) : [];
966
+ if (template.Outputs !== void 0) parts.push(template.Outputs);
967
+ const verdict = parts.some(partRereadsAccountId);
968
+ if (resources) accountRereadVerdicts.set(resources, {
969
+ outputs: template.Outputs,
970
+ verdict
971
+ });
972
+ return verdict;
973
+ }
974
+ const accountRereadVerdicts = /* @__PURE__ */ new WeakMap();
975
+ function partRereadsAccountId(part) {
976
+ let trigger = false;
977
+ let account = false;
978
+ const pending = [part];
979
+ while (pending.length > 0 && !(trigger && account)) {
980
+ const node = pending.pop();
981
+ if (Array.isArray(node)) {
982
+ pending.push(...node);
983
+ continue;
984
+ }
985
+ if (node === null || typeof node !== "object") continue;
986
+ for (const [key, value] of Object.entries(node)) {
987
+ if (REREAD_TRIGGERS.has(key)) trigger = true;
988
+ if (key === "Ref" && value === "AWS::AccountId") account = true;
989
+ if (key === "Fn::Sub") {
990
+ const text = Array.isArray(value) ? value[0] : value;
991
+ if (typeof text === "string" && text.includes("${AWS::AccountId}")) account = true;
992
+ }
993
+ pending.push(value);
994
+ }
995
+ }
996
+ return trigger && account;
997
+ }
998
+ const inheritedPseudoParameters = /* @__PURE__ */ new WeakMap();
999
+ const POISONED_PSEUDO_PARAMETERS = Symbol("cdkd.inherited-pseudo-parameters.poisoned");
1000
+ function recordInheritedPseudoParameters(inheritedSecrets, values) {
1001
+ let table = inheritedPseudoParameters.get(inheritedSecrets);
1002
+ if (table === POISONED_PSEUDO_PARAMETERS) return;
1003
+ if (table === void 0) {
1004
+ table = /* @__PURE__ */ new Map();
1005
+ inheritedPseudoParameters.set(inheritedSecrets, table);
1006
+ }
1007
+ for (const [name, value] of values) {
1008
+ if (!REREAD_PSEUDO_PARAMETERS.has(name) || typeof value !== "string") continue;
1009
+ const known = table.get(name);
1010
+ if (known !== void 0 && known !== value) {
1011
+ inheritedPseudoParameters.set(inheritedSecrets, POISONED_PSEUDO_PARAMETERS);
1012
+ return;
1013
+ }
1014
+ table.set(name, value);
1015
+ }
1016
+ }
1017
+ function pseudoBindingOf(parentSecrets) {
1018
+ const table = inheritedPseudoParameters.get(parentSecrets);
1019
+ return (name) => table instanceof Map && table.has(name) ? { value: table.get(name) } : void 0;
1020
+ }
1021
+ function inheritedPseudoValue(parentSecrets, name) {
1022
+ const bound = pseudoBindingOf(parentSecrets)(name)?.value;
1023
+ return typeof bound === "string" ? bound : void 0;
1024
+ }
1025
+ function inheritedPseudoSpan(childSecrets, key) {
1026
+ const reads = inheritedParameterReads.get(childSecrets);
1027
+ if (reads === void 0 || typeof reads === "symbol") return void 0;
1028
+ for (const name of REREAD_PSEUDO_PARAMETERS) if (crossStackSourceKey({ Ref: name }) === key) return inheritedPseudoValue(reads.parent, name);
1029
+ }
1030
+ const REREAD_TRIGGERS = /* @__PURE__ */ new Set([
1031
+ "Fn::Select",
1032
+ "Fn::Split",
1033
+ "Fn::If"
1034
+ ]);
1035
+ const MAX_REREAD_DEPTH = 16;
1036
+ const MAX_REREAD_CONDITIONS = 4;
1037
+ const REREAD_NO_VALUE = Symbol("cdkd.reread.no-value");
1038
+ function splitOutsideDynamicReferences(text, delimiter, standsForSecret) {
1039
+ if (delimiter === "") return text.split(delimiter);
1040
+ const spans = dynamicReferenceSpans(text).filter((span) => {
1041
+ const token = text.slice(span.start, span.end);
1042
+ return spanNamesResolvableService(token) && standsForSecret(token);
1043
+ });
1044
+ if (spans.length === 0) return text.split(delimiter);
1045
+ const pieces = [];
1046
+ let cursor = 0;
1047
+ let from = 0;
1048
+ for (;;) {
1049
+ const at = text.indexOf(delimiter, from);
1050
+ if (at === -1) break;
1051
+ const end = at + delimiter.length;
1052
+ const token = spans.find((span) => span.start < end && at < span.end);
1053
+ if (token !== void 0) {
1054
+ from = token.end;
1055
+ continue;
1056
+ }
1057
+ pieces.push(text.slice(cursor, at));
1058
+ cursor = end;
1059
+ from = end;
1060
+ }
1061
+ pieces.push(text.slice(cursor));
1062
+ return pieces;
1063
+ }
1064
+ function standsForInheritedSecret(parentSecrets, token) {
1065
+ return parentSecrets !== void 0 && resolvedPlaintextOf(parentSecrets, token) !== void 0;
1066
+ }
1067
+ const plainSplit = (text, delimiter) => text.split(delimiter);
1068
+ function evaluatePureRead(node, bind, pseudo, split, choose, depth = 0) {
1069
+ if (depth > MAX_REREAD_DEPTH) return void 0;
1070
+ const evaluate = (child) => evaluatePureRead(child, bind, pseudo, split, choose, depth + 1);
1071
+ if (typeof node === "string") return node.includes("{{resolve:") ? void 0 : { value: node };
1072
+ if (Array.isArray(node)) {
1073
+ const out = [];
1074
+ for (const element of node) {
1075
+ const evaluated = evaluate(element);
1076
+ if (evaluated === void 0 || evaluated.value === REREAD_NO_VALUE) return void 0;
1077
+ out.push(evaluated.value);
1078
+ }
1079
+ return { value: out };
1080
+ }
1081
+ if (!isPlainObject(node)) return void 0;
1082
+ const keys = Object.keys(node);
1083
+ if (keys.length !== 1) return void 0;
1084
+ const key = keys[0];
1085
+ const args = node[key];
1086
+ if (key === "Ref") {
1087
+ if (args === "AWS::NoValue") return { value: REREAD_NO_VALUE };
1088
+ return typeof args === "string" ? bindReadName(args, bind, pseudo) : void 0;
1089
+ }
1090
+ if (key === "Fn::If") {
1091
+ if (!Array.isArray(args) || args.length !== 3 || typeof args[0] !== "string") return void 0;
1092
+ const tookTrue = choose(args[0], node);
1093
+ if (tookTrue === void 0) return void 0;
1094
+ return evaluate(tookTrue ? args[1] : args[2]);
1095
+ }
1096
+ if (key === "Fn::Sub") return evaluatePureSub(args, bind, pseudo, evaluate);
1097
+ if (!Array.isArray(args) || args.length !== 2) return void 0;
1098
+ if (key === "Fn::Select") {
1099
+ const index = args[0];
1100
+ const position = typeof index === "number" ? index : typeof index === "string" && /^(0|[1-9][0-9]*)$/.test(index) ? Number(index) : void 0;
1101
+ if (position === void 0 || !Number.isSafeInteger(position) || position < 0) return void 0;
1102
+ const list = evaluate(args[1]);
1103
+ if (list === void 0 || !Array.isArray(list.value) || position >= list.value.length) return;
1104
+ return { value: list.value[position] };
1105
+ }
1106
+ const delimiter = args[0];
1107
+ if (typeof delimiter !== "string") return void 0;
1108
+ if (key === "Fn::Split") {
1109
+ const value = evaluate(args[1]);
1110
+ if (value === void 0 || typeof value.value !== "string") return void 0;
1111
+ return { value: split(value.value, delimiter) };
1112
+ }
1113
+ if (key === "Fn::Join") {
1114
+ const parts = evaluate(args[1]);
1115
+ if (parts === void 0 || !Array.isArray(parts.value)) return void 0;
1116
+ if (!parts.value.every((part) => typeof part === "string")) return void 0;
1117
+ return { value: parts.value.join(delimiter) };
1118
+ }
1119
+ }
1120
+ function evaluatePureSub(args, bind, pseudo, evaluate) {
1121
+ const [text, variables] = typeof args === "string" ? [args, {}] : Array.isArray(args) && args.length === 2 ? [args[0], args[1]] : [void 0, void 0];
1122
+ if (typeof text !== "string" || text.includes("{{resolve:") || !isPlainObject(variables)) return;
1123
+ const bound = /* @__PURE__ */ new Map();
1124
+ for (const [name, variable] of Object.entries(variables)) {
1125
+ const evaluated = evaluate(variable);
1126
+ if (evaluated === void 0 || typeof evaluated.value !== "string") return void 0;
1127
+ bound.set(name, evaluated.value);
1128
+ }
1129
+ let out = "";
1130
+ let cursor = 0;
1131
+ for (const match of text.matchAll(/\$\{(!)?([^}]*)\}/g)) {
1132
+ const name = match[2] ?? "";
1133
+ let replacement;
1134
+ if (match[1] === "!") replacement = `\${${name}}`;
1135
+ else if (name === "") replacement = match[0];
1136
+ else if (bound.has(name)) replacement = bound.get(name);
1137
+ else {
1138
+ if (name.includes(".")) return void 0;
1139
+ const value = bindReadName(name, bind, pseudo);
1140
+ if (value === void 0 || typeof value.value !== "string") return void 0;
1141
+ replacement = value.value;
1142
+ }
1143
+ out += text.slice(cursor, match.index) + replacement;
1144
+ cursor = match.index + match[0].length;
1145
+ }
1146
+ return { value: out + text.slice(cursor) };
1147
+ }
1148
+ function bindReadName(name, bind, pseudo) {
1149
+ if (!name.startsWith("AWS::")) return bind(name);
1150
+ return bind(name) === void 0 ? pseudo(name) : void 0;
1151
+ }
1152
+ function collectRereadConditions(node, names, depth = 0) {
1153
+ if (depth > MAX_REREAD_DEPTH) return false;
1154
+ if (Array.isArray(node)) return node.every((element) => collectRereadConditions(element, names, depth + 1));
1155
+ if (!isPlainObject(node)) return true;
1156
+ if (Object.hasOwn(node, "Fn::If")) {
1157
+ const args = node["Fn::If"];
1158
+ if (!Array.isArray(args) || typeof args[0] !== "string") return false;
1159
+ names.add(args[0]);
1160
+ }
1161
+ return Object.values(node).every((child) => collectRereadConditions(child, names, depth + 1));
1162
+ }
1163
+ function holdsRereadTrigger(node, depth = 0) {
1164
+ if (depth > MAX_REREAD_DEPTH) return false;
1165
+ if (Array.isArray(node)) return node.some((element) => holdsRereadTrigger(element, depth + 1));
1166
+ if (!isPlainObject(node)) return false;
1167
+ return Object.entries(node).some(([key, child]) => REREAD_TRIGGERS.has(key) || holdsRereadTrigger(child, depth + 1));
1168
+ }
1169
+ function rendersReadTo(spelling, parentSecrets, value) {
1170
+ if (typeof spelling === "string" && typeof value === "string") {
1171
+ if (spelling === value) return redactForState(spelling, {
1172
+ provenance: "value-scan",
1173
+ secrets: parentSecrets
1174
+ }) === spelling;
1175
+ return rendersLiteralTo(spelling, parentSecrets, value);
1176
+ }
1177
+ if (Array.isArray(spelling) && Array.isArray(value) && spelling.length === value.length) return spelling.every((element, i) => rendersReadTo(element, parentSecrets, value[i]));
1178
+ return false;
1179
+ }
1180
+ const REREAD_UNBOUND = Symbol("cdkd.reread.unbound");
1181
+ const REREAD_PARAMETER_FREE = Symbol("cdkd.reread.parameter-free");
1182
+ function diffSideReading(bind, parentSecrets) {
1183
+ return {
1184
+ bind: (name) => {
1185
+ const bound = bind(name);
1186
+ return bound && { value: redactInheritedParameterValue(parentSecrets, name, bound.value) };
1187
+ },
1188
+ split: (text, delimiter) => splitOutsideDynamicReferences(text, delimiter, (token) => standsForInheritedSecret(parentSecrets, token))
1189
+ };
1190
+ }
1191
+ function rereadUnder(source, bind, parentSecrets, choose) {
1192
+ const pseudo = pseudoBindingOf(parentSecrets);
1193
+ const resolved = evaluatePureRead(source, bind, pseudo, plainSplit, choose);
1194
+ if (resolved === void 0) return void 0;
1195
+ const diffSide = diffSideReading(bind, parentSecrets);
1196
+ const redacted = evaluatePureRead(source, diffSide.bind, pseudo, diffSide.split, choose);
1197
+ if (redacted === void 0) return void 0;
1198
+ if (resolved.value === REREAD_NO_VALUE || redacted.value === REREAD_NO_VALUE) return resolved.value === redacted.value ? REREAD_NO_VALUE : void 0;
1199
+ if (!rendersReadTo(redacted.value, parentSecrets, resolved.value)) return void 0;
1200
+ return {
1201
+ resolved: resolved.value,
1202
+ redacted: redacted.value
1203
+ };
1204
+ }
1205
+ function namesParameterOnBranches(node, choose, isParameter, depth = 0) {
1206
+ if (depth > MAX_REREAD_DEPTH) return true;
1207
+ if (typeof node === "string") return node.includes("{{resolve:");
1208
+ if (Array.isArray(node)) return node.some((element) => namesParameterOnBranches(element, choose, isParameter, depth + 1));
1209
+ if (!isPlainObject(node)) return false;
1210
+ if (Object.hasOwn(node, "Fn::If")) {
1211
+ const args = node["Fn::If"];
1212
+ if (Object.keys(node).length !== 1) return true;
1213
+ if (!Array.isArray(args) || args.length !== 3 || typeof args[0] !== "string") return true;
1214
+ const tookTrue = choose(args[0], node);
1215
+ if (tookTrue === void 0) return true;
1216
+ return namesParameterOnBranches(tookTrue ? args[1] : args[2], choose, isParameter, depth + 1);
1217
+ }
1218
+ if (typeof node["Ref"] === "string" && isParameter(node["Ref"])) return true;
1219
+ const sub = node["Fn::Sub"];
1220
+ const text = typeof sub === "string" ? sub : Array.isArray(sub) ? sub[0] : void 0;
1221
+ if (typeof text === "string") {
1222
+ for (const match of text.matchAll(/\$\{([^}!][^}]*)\}/g)) if (isParameter(match[1].trim()) || isParameter(match[1])) return true;
1223
+ }
1224
+ return Object.values(node).some((child) => namesParameterOnBranches(child, choose, isParameter, depth + 1));
1225
+ }
1226
+ function rereadAssignments(source, bind, parentSecrets, untakenWhenUnbound = () => false, parameterFree) {
1227
+ if (!holdsRereadTrigger(source)) return void 0;
1228
+ const conditions = /* @__PURE__ */ new Set();
1229
+ if (!collectRereadConditions(source, conditions)) return void 0;
1230
+ if (conditions.size > MAX_REREAD_CONDITIONS) return void 0;
1231
+ const names = [...conditions];
1232
+ const out = [];
1233
+ for (let assignment = 0; assignment < 2 ** names.length; assignment++) {
1234
+ const choose = (condition) => Math.floor(assignment / 2 ** names.indexOf(condition)) % 2 === 1;
1235
+ let unbound = false;
1236
+ const tracked = (name) => {
1237
+ const bound = bind(name);
1238
+ if (bound === void 0 && untakenWhenUnbound(name)) unbound = true;
1239
+ return bound;
1240
+ };
1241
+ const reread = rereadUnder(source, tracked, parentSecrets, choose);
1242
+ if (unbound) out.push(REREAD_UNBOUND);
1243
+ else if (reread !== void 0) out.push(reread);
1244
+ else if (parameterFree !== void 0 && !namesParameterOnBranches(source, choose, parameterFree)) out.push(REREAD_PARAMETER_FREE);
1245
+ else out.push(void 0);
1246
+ }
1247
+ return out;
1248
+ }
1249
+ function rereadParameterExpression(source, bind, parentSecrets) {
1250
+ const assignments = rereadAssignments(source, bind, parentSecrets, void 0, (name) => bind(name) !== void 0);
1251
+ if (assignments === void 0 || assignments.includes(void 0) || assignments.includes(REREAD_UNBOUND)) return;
1252
+ return assignments.find((entry) => entry !== void 0 && entry !== REREAD_NO_VALUE && entry !== REREAD_UNBOUND && entry !== REREAD_PARAMETER_FREE);
1253
+ }
1254
+ function ambiguousRereads(rereads) {
1255
+ return rereads.some((a, i) => rereads.slice(i + 1).some((b) => deepEqualJsonValue(a.resolved, b.resolved) && !deepEqualJsonValue(a.redacted, b.redacted)));
1256
+ }
1257
+ function positionByParameterReread(bag, source, secrets) {
1258
+ const reads = inheritedParameterReads.get(secrets);
1259
+ if (reads === void 0 || typeof reads === "symbol") return void 0;
1260
+ if (typeof bag !== "string" && !Array.isArray(bag)) return void 0;
1261
+ if (!holdsRereadTrigger(source)) return void 0;
1262
+ const bind = (name) => reads.values.has(name) ? { value: reads.values.get(name) } : void 0;
1263
+ let unrecorded = false;
1264
+ const recorded = rereadUnder(source, bind, reads.parent, (_, node) => {
1265
+ const taken = takenIfBranchOf(secrets, node);
1266
+ if (taken === void 0) unrecorded = true;
1267
+ return taken;
1268
+ });
1269
+ let answer;
1270
+ if (!unrecorded) {
1271
+ if (recorded === void 0 || recorded === REREAD_NO_VALUE || !deepEqualJsonValue(recorded.resolved, bag)) return;
1272
+ answer = recorded.redacted;
1273
+ } else {
1274
+ const declared = reads.declared;
1275
+ const assignments = rereadAssignments(source, bind, reads.parent, (name) => declared?.has(name) === true);
1276
+ if (assignments === void 0 || assignments.includes(void 0)) return void 0;
1277
+ const matching = assignments.filter((entry) => entry !== void 0 && entry !== REREAD_NO_VALUE && entry !== REREAD_UNBOUND && entry !== REREAD_PARAMETER_FREE && deepEqualJsonValue(entry.resolved, bag));
1278
+ if (matching.length === 0 || ambiguousRereads(matching)) return void 0;
1279
+ answer = matching[0].redacted;
1280
+ }
1281
+ if (carriesChildOnlyPlaintext(bag, secrets, reads.parent)) return void 0;
1282
+ if (!deepEqualJsonValue(redactForState(answer, {
1283
+ provenance: "value-scan",
1284
+ secrets
1285
+ }), answer)) return void 0;
1286
+ return answer;
1287
+ }
1288
+ function carriesChildOnlyPlaintext(value, childSecrets, parentSecrets) {
1289
+ const parentNeedles = new Set(substringNeedlesOf(parentSecrets));
1290
+ const childNeedles = new Set(substringNeedlesOf(childSecrets));
1291
+ const childOnly = [...childSecrets.keys()].filter((plaintext) => plaintext !== "" && (!parentSecrets.has(plaintext) || childNeedles.has(plaintext) && !parentNeedles.has(plaintext)));
1292
+ for (const needle of parentNeedles) if (needle.length >= 4 && !childSecrets.has(needle)) childOnly.push(needle);
1293
+ if (childOnly.length === 0) return false;
1294
+ const visit = (node) => {
1295
+ if (typeof node === "string") return childOnly.some((plaintext) => node.includes(plaintext));
1296
+ if (Array.isArray(node)) return node.some(visit);
1297
+ if (node !== null && typeof node === "object") return Object.values(node).some(visit);
1298
+ return false;
1299
+ };
1300
+ return visit(value);
1301
+ }
1302
+
1303
+ //#endregion
1304
+ //#region src/deployment/secret-redaction/placeholder-positions.ts
1305
+ function keyedPart(source) {
1306
+ const key = crossStackSourceKey(source);
1307
+ return key === void 0 ? UNKNOWN_PART : { key };
1308
+ }
1309
+ const MAX_PLACEHOLDER_DEPTH = 8;
1310
+ function pushPlaceholderPart(out, part, budget) {
1311
+ if (budget.remaining <= 0) {
1312
+ budget.exhausted = true;
1313
+ return;
1314
+ }
1315
+ budget.remaining -= 1;
1316
+ out.push(part);
1317
+ }
1318
+ function appendValuePlaceholderParts(value, out, budget, depth) {
1319
+ if (budget.exhausted) return;
1320
+ const literal = joinPartLiteralText(value);
1321
+ if (literal !== void 0) return pushPlaceholderPart(out, literal, budget);
1322
+ if (isPlainObject(value) && Object.keys(value).length === 1) {
1323
+ const ref = value["Ref"];
1324
+ if (typeof ref === "string" && ref !== "") return pushPlaceholderPart(out, keyedPart({ Ref: ref }), budget);
1325
+ if (depth >= MAX_PLACEHOLDER_DEPTH) {
1326
+ budget.exhausted = true;
1327
+ return;
1328
+ }
1329
+ if (appendParameterPlaceholderParts(value, out, budget, depth + 1)) return;
1330
+ return pushPlaceholderPart(out, keyedPart(value), budget);
1331
+ }
1332
+ pushPlaceholderPart(out, UNKNOWN_PART, budget);
1333
+ }
1334
+ function appendParameterPlaceholderParts(source, out, budget, depth) {
1335
+ const keys = Object.keys(source);
1336
+ if (keys.length !== 1) return false;
1337
+ const key = keys[0];
1338
+ if (key === "Fn::Join") {
1339
+ const args = source[key];
1340
+ if (!Array.isArray(args) || args.length !== 2) return false;
1341
+ const [delimiter, parts] = args;
1342
+ if (typeof delimiter !== "string" || !Array.isArray(parts)) return false;
1343
+ for (let index = 0; index < parts.length && !budget.exhausted; index++) {
1344
+ if (index > 0) pushPlaceholderPart(out, delimiter, budget);
1345
+ appendValuePlaceholderParts(parts[index], out, budget, depth);
1346
+ }
1347
+ return true;
1348
+ }
1349
+ if (key === "Fn::Sub") {
1350
+ const args = source[key];
1351
+ let template;
1352
+ let variables = {};
1353
+ if (typeof args === "string") template = args;
1354
+ else if (Array.isArray(args) && args.length === 2 && isPlainObject(args[1])) {
1355
+ template = args[0];
1356
+ variables = args[1];
1357
+ } else return false;
1358
+ if (typeof template !== "string") return false;
1359
+ let cursor = 0;
1360
+ for (const hit of template.matchAll(/\$\{(!)?([^}]*)\}/g)) {
1361
+ if (budget.exhausted) return true;
1362
+ pushPlaceholderPart(out, template.slice(cursor, hit.index), budget);
1363
+ cursor = hit.index + hit[0].length;
1364
+ const name = hit[2] ?? "";
1365
+ if (hit[1] === "!") pushPlaceholderPart(out, `\${${name}}`, budget);
1366
+ else if (name === "") pushPlaceholderPart(out, hit[0], budget);
1367
+ else if (Object.hasOwn(variables, name)) appendValuePlaceholderParts(variables[name], out, budget, depth);
1368
+ else if (name.includes(".")) pushPlaceholderPart(out, keyedPart({ "Fn::GetAtt": name }), budget);
1369
+ else pushPlaceholderPart(out, keyedPart({ Ref: name }), budget);
1370
+ }
1371
+ pushPlaceholderPart(out, template.slice(cursor), budget);
1372
+ return true;
1373
+ }
1374
+ return false;
1375
+ }
1376
+ function parameterPlaceholderParts(source, bag) {
1377
+ const budget = {
1378
+ remaining: bag.length * 2 + 16,
1379
+ exhausted: false
1380
+ };
1381
+ const out = [];
1382
+ if (!appendParameterPlaceholderParts(source, out, budget, 0)) return void 0;
1383
+ return budget.exhausted ? void 0 : out;
1384
+ }
1385
+ function positionByParameterPlaceholders(bag, source, secrets) {
1386
+ const associations = crossStackAssociations.get(secrets);
1387
+ const spanned = positionByRecordedParameterSpans(bag, source, secrets, associations);
1388
+ if (spanned !== void 0) return spanned;
1389
+ const parts = parameterPlaceholderParts(source, bag);
1390
+ if (parts === void 0) return void 0;
1391
+ const before = ["", ""];
1392
+ const after = ["", ""];
1393
+ let side = before;
1394
+ let unknown = false;
1395
+ let certified = 0;
1396
+ const spans = [];
1397
+ for (const part of parts) {
1398
+ let rendered;
1399
+ let persisted;
1400
+ if (typeof part === "string") {
1401
+ rendered = part;
1402
+ persisted = part;
1403
+ } else {
1404
+ let expression;
1405
+ let plaintext = "";
1406
+ if (part !== UNKNOWN_PART) {
1407
+ const association = associations?.get(part.key);
1408
+ if (association !== void 0 && typeof association !== "symbol") {
1409
+ plaintext = association.plaintext;
1410
+ expression = certifiedExpressionForLeaf(secrets, association, plaintext);
1411
+ }
1412
+ if (expression === void 0) {
1413
+ const inherited = inheritedRenderedSpan(secrets, part.key);
1414
+ if (inherited !== void 0) {
1415
+ plaintext = inherited.value;
1416
+ expression = inherited.spelling;
1417
+ }
1418
+ }
1419
+ }
1420
+ const pseudo = expression === void 0 && part !== UNKNOWN_PART ? inheritedPseudoSpan(secrets, part.key) : void 0;
1421
+ if (pseudo !== void 0) {
1422
+ side[0] += pseudo;
1423
+ side[1] += pseudo;
1424
+ continue;
1425
+ }
1426
+ if (expression === void 0) {
1427
+ if (unknown) return void 0;
1428
+ unknown = true;
1429
+ side = after;
1430
+ continue;
1431
+ }
1432
+ rendered = plaintext;
1433
+ persisted = expression;
1434
+ certified += 1;
1435
+ spans.push([
1436
+ side,
1437
+ side[0].length,
1438
+ plaintext.length
1439
+ ]);
1440
+ }
1441
+ side[0] += rendered;
1442
+ side[1] += persisted;
1443
+ }
1444
+ if (certified === 0) return void 0;
1445
+ let persisted;
1446
+ if (!unknown) {
1447
+ if (before[0] !== bag) return void 0;
1448
+ persisted = before[1];
1449
+ } else {
1450
+ if (bag.length < before[0].length + after[0].length) return void 0;
1451
+ if (!bag.startsWith(before[0]) || !bag.endsWith(after[0])) return void 0;
1452
+ const middle = bag.slice(before[0].length, bag.length - after[0].length);
1453
+ if (redactForState(middle, {
1454
+ provenance: "value-scan",
1455
+ secrets
1456
+ }) !== middle) return void 0;
1457
+ persisted = before[1] + middle + after[1];
1458
+ }
1459
+ const afterStart = bag.length - after[0].length;
1460
+ return acceptedPlaceholderRendering(bag, persisted, spans.map(([side, offset, length]) => [(side === before ? 0 : afterStart) + offset, length]), secrets);
1461
+ }
1462
+ function acceptedPlaceholderRendering(bag, persisted, spans, secrets) {
1463
+ for (const plaintext of secrets.keys()) {
1464
+ if (plaintext.length < 4) continue;
1465
+ for (let at = bag.indexOf(plaintext); at !== -1; at = bag.indexOf(plaintext, at + 1)) {
1466
+ const end = at + plaintext.length;
1467
+ for (const [start, length] of spans) if (at < start + length && end > start && (at < start || end > start + length)) return void 0;
1468
+ }
1469
+ }
1470
+ if (redactForState(persisted, {
1471
+ provenance: "value-scan",
1472
+ secrets
1473
+ }) !== persisted) return void 0;
1474
+ return persisted;
1475
+ }
1476
+ function positionByRecordedParameterSpans(bag, source, secrets, associations) {
1477
+ const resolution = intrinsicLeafResolutionOf(secrets, source);
1478
+ if (resolution === void 0 || resolution.output !== bag) return void 0;
1479
+ const recorded = resolution.parameterSpans;
1480
+ if (recorded === void 0) return void 0;
1481
+ let previousEnd = 0;
1482
+ let cursor = 0;
1483
+ let persisted = "";
1484
+ const gaps = [];
1485
+ const certified = [];
1486
+ for (const { start, length, key } of recorded) {
1487
+ if (!(start >= previousEnd && length > 0 && start + length <= bag.length)) return void 0;
1488
+ previousEnd = start + length;
1489
+ const text = bag.slice(start, start + length);
1490
+ const association = associations?.get(key);
1491
+ const expression = (association !== void 0 && typeof association !== "symbol" ? certifiedExpressionForLeaf(secrets, association, text) : void 0) ?? inheritedRenderedSpan(secrets, key, text)?.spelling;
1492
+ if (expression === void 0) continue;
1493
+ const gap = bag.slice(cursor, start);
1494
+ gaps.push(gap);
1495
+ persisted += gap + expression;
1496
+ certified.push([start, length]);
1497
+ cursor = start + length;
1498
+ }
1499
+ if (certified.length === 0) return void 0;
1500
+ const tail = bag.slice(cursor);
1501
+ gaps.push(tail);
1502
+ persisted += tail;
1503
+ for (const gap of gaps) if (redactForState(gap, {
1504
+ provenance: "value-scan",
1505
+ secrets
1506
+ }) !== gap) return void 0;
1507
+ return acceptedPlaceholderRendering(bag, persisted, certified, secrets);
1508
+ }
1509
+
1510
+ //#endregion
1511
+ //#region src/deployment/secret-redaction/identity-keys.ts
1512
+ const ARRAY_IDENTITY_KEYS = ["Name", "Key"];
1513
+ function isUniquelyKeyedBy(items, key) {
1514
+ if (items.length === 0) return false;
1515
+ const seen = /* @__PURE__ */ new Set();
1516
+ for (const item of items) {
1517
+ if (!isPlainObject(item) || !Object.hasOwn(item, key)) return false;
1518
+ const id = item[key];
1519
+ if (typeof id !== "string" || id === "") return false;
1520
+ if (seen.has(id)) return false;
1521
+ seen.add(id);
1522
+ }
1523
+ return true;
1524
+ }
1525
+ function identityKeyFor(bag, source) {
1526
+ for (const key of ARRAY_IDENTITY_KEYS) if (isUniquelyKeyedBy(bag, key) && isUniquelyKeyedBy(source, key)) return key;
1527
+ }
1528
+ function positionListByCrossStackSource(bag, source, secrets) {
1529
+ const association = associationForSource(source, secrets);
1530
+ if (association === void 0) return void 0;
1531
+ return certifiedListForLeaf(secrets, association, bag);
1532
+ }
1533
+
1534
+ //#endregion
1535
+ //#region src/deployment/secret-redaction/redact-path.ts
1536
+ const NEVER_DROPPING_INTRINSICS = /* @__PURE__ */ new Set([
1537
+ "Fn::Join",
1538
+ "Fn::Sub",
1539
+ "Fn::Select",
1540
+ "Fn::Split",
1541
+ "Fn::GetAtt"
1542
+ ]);
1543
+ function arrayElementDrop(element, depth = 0) {
1544
+ if (depth > 16) return "unknown";
1545
+ if (!isPlainObject(element)) return "never";
1546
+ if (Object.hasOwn(element, "Ref")) return element["Ref"] === "AWS::NoValue" ? "always" : "never";
1547
+ const keys = Object.keys(element);
1548
+ if (keys.filter((key) => key.startsWith("Fn::") || key === "Condition").length === 0) return "never";
1549
+ if (keys.length !== 1) return "unknown";
1550
+ if (NEVER_DROPPING_INTRINSICS.has(keys[0])) return "never";
1551
+ if (keys[0] !== "Fn::If") return "unknown";
1552
+ const args = element["Fn::If"];
1553
+ if (!Array.isArray(args) || args.length !== 3) return "unknown";
1554
+ const branches = [arrayElementDrop(args[1], depth + 1), arrayElementDrop(args[2], depth + 1)];
1555
+ if (branches.includes("unknown")) return "unknown";
1556
+ if (branches.every((branch) => branch === "never")) return "never";
1557
+ if (branches.every((branch) => branch === "always")) return "always";
1558
+ return "maybe";
1559
+ }
1560
+ function sourceElementsKeptFor(source, bagLength, secrets) {
1561
+ const recorded = secrets && droppedArrayElementsOf(secrets, source);
1562
+ if (recorded !== void 0) {
1563
+ if (source.length - recorded.length !== bagLength) return void 0;
1564
+ return source.filter((_, i) => !recorded.includes(i));
1565
+ }
1566
+ const drops = source.map((element) => arrayElementDrop(element));
1567
+ if (drops.includes("unknown")) return void 0;
1568
+ const always = drops.filter((drop) => drop === "always").length;
1569
+ const maybe = drops.filter((drop) => drop === "maybe").length;
1570
+ const dropped = source.length - bagLength;
1571
+ let dropMaybe;
1572
+ if (dropped === always) dropMaybe = false;
1573
+ else if (dropped === always + maybe) dropMaybe = true;
1574
+ else return void 0;
1575
+ return source.filter((_, i) => drops[i] === "never" || drops[i] === "maybe" && !dropMaybe);
1576
+ }
1577
+ function keptLiteralsCorroborate(kept, bag, secrets) {
1578
+ return kept.every((element, i) => keptElementCorroborates(element, bag[i], secrets));
1579
+ }
1580
+ function isIntrinsicNode(value) {
1581
+ if (!isPlainObject(value)) return false;
1582
+ const keys = Object.keys(value);
1583
+ return keys.length === 1 && (keys[0] === "Ref" || keys[0].startsWith("Fn::"));
1584
+ }
1585
+ function keptElementCorroborates(source, bag, secrets, depth = 0) {
1586
+ if (depth > 16) return false;
1587
+ if (typeof source === "string") {
1588
+ if (!source.includes("{{resolve:")) return source === bag;
1589
+ return isSingleDynamicReferenceToken(source) && resolvedPlaintextOf(secrets, source) === bag;
1590
+ }
1591
+ if (source === null || typeof source !== "object") return source === bag;
1592
+ if (isIntrinsicNode(source)) return true;
1593
+ if (Array.isArray(source)) return Array.isArray(bag) && bag.length === source.length && source.every((element, i) => keptElementCorroborates(element, bag[i], secrets, depth + 1));
1594
+ if (!isPlainObject(source) || !isPlainObject(bag)) return false;
1595
+ if (Object.keys(bag).some((key) => !Object.hasOwn(source, key))) return false;
1596
+ return Object.entries(source).every(([key, value]) => Object.hasOwn(bag, key) ? keptElementCorroborates(value, bag[key], secrets, depth + 1) : arrayElementDrop(value) === "always" || arrayElementDrop(value) === "maybe");
1597
+ }
1598
+ function corroboratesWhateverResolves(source, depth = 0) {
1599
+ if (depth > 16) return false;
1600
+ if (typeof source === "string") return !source.includes("{{resolve:");
1601
+ if (source === null || typeof source !== "object") return true;
1602
+ if (isIntrinsicNode(source)) return true;
1603
+ if (Array.isArray(source)) return source.every((element) => arrayElementDrop(element) === "never" && corroboratesWhateverResolves(element, depth + 1));
1604
+ if (!isPlainObject(source)) return false;
1605
+ if (Object.keys(source).some((key) => key === "Ref" || key.startsWith("Fn::"))) return false;
1606
+ return Object.values(source).every((value) => arrayElementDrop(value) !== "unknown" && corroboratesWhateverResolves(value, depth + 1));
1607
+ }
1608
+ function arrayPairsWhateverDrops(source) {
1609
+ if (!source.some((element) => isPlainObject(element) && (Object.hasOwn(element, "Fn::If") || element["Ref"] === "AWS::NoValue" || Array.isArray(element["Fn::FindInMap"]) && element["Fn::FindInMap"].length > 3))) return true;
1610
+ return source.every((element) => corroboratesWhateverResolves(element));
1611
+ }
1612
+ function redactByPath(bag, source, secrets, rules, secretExpressions, bagIsSameGeneration) {
1613
+ if (isDynamicReferenceString(source) && typeof bag === "string") {
1614
+ if (isSingleDynamicReferenceToken(source) && (rules.trustAnyExpression || isKnownSecretExpression(source, secretExpressions))) {
1615
+ if (!rules.sourceIsSameGeneration && isSingleDynamicReferenceToken(bag)) {
1616
+ if (!spanNamesResolvableService(bag)) return redactForState(bag, {
1617
+ provenance: "value-scan",
1618
+ secrets
1619
+ });
1620
+ return secrets.get(bag) ?? bag;
1621
+ }
1622
+ return source;
1623
+ }
1624
+ return positionByEmbeddedSpan(bag, source, secrets, bagIsSameGeneration);
1625
+ }
1626
+ if ((typeof bag === "string" || Array.isArray(bag)) && isPlainObject(source)) {
1627
+ const inherited = positionByInheritedParameter(bag, source, secrets);
1628
+ if (inherited !== void 0) return inherited;
1629
+ const reread = positionByParameterReread(bag, source, secrets);
1630
+ if (reread !== void 0) return reread;
1631
+ }
1632
+ if (typeof bag === "string" && isPlainObject(source)) {
1633
+ const certified = positionByCrossStackSource(bag, source, secrets);
1634
+ if (certified !== void 0) return certified;
1635
+ const placeheld = positionByParameterPlaceholders(bag, source, secrets);
1636
+ if (placeheld !== void 0) return placeheld;
1637
+ const positioned = positionByIntrinsicSkeleton(bag, source, secrets, secretExpressions);
1638
+ if (positioned !== void 0) return positioned;
1639
+ const framed = positionByIntrinsicFrame(bag, source, secrets, secretExpressions, bagIsSameGeneration);
1640
+ if (framed !== void 0) return framed;
1641
+ }
1642
+ if (Array.isArray(bag) && isPlainObject(source)) {
1643
+ const positionedList = positionListByCrossStackSource(bag, source, secrets);
1644
+ if (positionedList !== void 0) return positionedList;
1645
+ }
1646
+ if (Array.isArray(bag) && Array.isArray(source)) {
1647
+ const key = identityKeyFor(bag, source);
1648
+ if (key !== void 0) {
1649
+ const sourceIndexById = /* @__PURE__ */ new Map();
1650
+ source.forEach((item, i) => {
1651
+ sourceIndexById.set(item[key], i);
1652
+ });
1653
+ let orderPreserved = true;
1654
+ const partnerIndex = bag.map((item, i) => {
1655
+ const j = sourceIndexById.get(item[key]);
1656
+ if (j === void 0) return -1;
1657
+ if (j !== i) orderPreserved = false;
1658
+ return j;
1659
+ });
1660
+ const positionalIsExact = rules.descendArrays && bag.length === source.length && orderPreserved;
1661
+ return bag.map((item, i) => {
1662
+ const j = partnerIndex[i];
1663
+ if (j >= 0) return redactByPath(item, source[j], secrets, rules, secretExpressions, bagIsSameGeneration);
1664
+ if (positionalIsExact) return redactByPath(item, source[i], secrets, rules, secretExpressions, bagIsSameGeneration);
1665
+ return redactForState(item, {
1666
+ provenance: "value-scan",
1667
+ secrets
1668
+ });
1669
+ });
1670
+ }
1671
+ if (rules.descendArrays && bag.length === source.length) return bag.map((item, i) => redactByPath(item, source[i], secrets, rules, secretExpressions, bagIsSameGeneration));
1672
+ if (rules.descendArrays && bag.length < source.length) {
1673
+ const kept = sourceElementsKeptFor(source, bag.length, secrets);
1674
+ if (kept !== void 0 && keptLiteralsCorroborate(kept, bag, secrets)) return bag.map((item, i) => redactByPath(item, kept[i], secrets, rules, secretExpressions, bagIsSameGeneration));
1675
+ }
1676
+ }
1677
+ if (isPlainObject(bag) && hasPlainPrototype(bag) && isPlainObject(source)) {
1678
+ const out = Object.create(null);
1679
+ for (const [k, v] of Object.entries(bag)) out[k] = Object.hasOwn(source, k) ? redactByPath(v, source[k], secrets, rules, secretExpressions, bagIsSameGeneration) : redactForState(v, {
1680
+ provenance: "value-scan",
1681
+ secrets
1682
+ });
1683
+ return out;
1684
+ }
1685
+ return redactForState(bag, {
1686
+ provenance: "value-scan",
1687
+ secrets
1688
+ });
1689
+ }
1690
+ function isReadbackProjectedFromState(rules) {
1691
+ return rules.trustAnyExpression && !rules.descendArrays && rules.sourceIsSameGeneration;
1692
+ }
1693
+ function subtreeHasDynamicReference(value) {
1694
+ if (isDynamicReferenceString(value)) return true;
1695
+ if (Array.isArray(value)) return value.some(subtreeHasDynamicReference);
1696
+ if (isPlainObject(value)) return Object.values(value).some(subtreeHasDynamicReference);
1697
+ return false;
1698
+ }
1699
+ function pathCrossesDottedKey(bag, path) {
1700
+ const segments = path.split(".");
1701
+ let cursor = bag;
1702
+ for (let i = 0; i < segments.length; i++) {
1703
+ if (cursor === null || typeof cursor !== "object" || Array.isArray(cursor)) return false;
1704
+ const rest = segments.slice(i).join(".");
1705
+ for (const key of Object.keys(cursor)) if (key.includes(".") && (rest === key || rest.startsWith(`${key}.`))) return true;
1706
+ if (!Object.hasOwn(cursor, segments[i])) return false;
1707
+ cursor = cursor[segments[i]];
1708
+ }
1709
+ return false;
1710
+ }
1711
+ function isUncertifiedBaselineMaskPosition(properties, path) {
1712
+ if (pathCrossesDottedKey(properties, path)) return false;
1713
+ let cursor = properties;
1714
+ for (const segment of path.split(".")) {
1715
+ if (typeof cursor === "string") break;
1716
+ if (cursor === null || typeof cursor !== "object" || Array.isArray(cursor) || !Object.hasOwn(cursor, segment)) return false;
1717
+ cursor = cursor[segment];
1718
+ }
1719
+ if (typeof cursor === "string") return isDynamicReferenceString(cursor) && cursor !== "***";
1720
+ return subtreeHasDynamicReference(cursor) && !carriesSecretMask(cursor);
1721
+ }
1722
+ const DYNAMIC_REFERENCE_TOKEN_SCAN = new RegExp(`\\{\\{resolve:${DYNAMIC_REFERENCE_INNER}\\}\\}`, "g");
1723
+ function dynamicReferenceTokens(value) {
1724
+ return value.match(DYNAMIC_REFERENCE_TOKEN_SCAN) ?? [];
1725
+ }
1726
+ function dynamicReferenceSpans(value) {
1727
+ DYNAMIC_REFERENCE_TOKEN_SCAN.lastIndex = 0;
1728
+ const spans = [];
1729
+ for (const match of value.matchAll(DYNAMIC_REFERENCE_TOKEN_SCAN)) spans.push({
1730
+ start: match.index,
1731
+ end: match.index + match[0].length
1732
+ });
1733
+ return spans;
1734
+ }
1735
+ function mixedLeafMayCarryPublicReference(source, secrets, readback) {
1736
+ if (secrets.size === 0) return mixedLeafProvenPublic(source, secrets, readback);
1737
+ return dynamicReferenceTokens(source).some((token) => token.startsWith("{{resolve:ssm:") && !isRecordedSecretExpression(token));
1738
+ }
1739
+ function mixedLeafProvenPublic(source, secrets, readback) {
1740
+ const tokens = dynamicReferenceTokens(source);
1741
+ if (tokens.length === 0) return false;
1742
+ const values = /* @__PURE__ */ new Map();
1743
+ for (const token of tokens) {
1744
+ if (!token.startsWith("{{resolve:ssm:")) return false;
1745
+ const value = provenPublicValue(secrets, token);
1746
+ if (value === void 0) return false;
1747
+ values.set(token, value);
1748
+ }
1749
+ DYNAMIC_REFERENCE_TOKEN_SCAN.lastIndex = 0;
1750
+ return source.replace(DYNAMIC_REFERENCE_TOKEN_SCAN, (token) => values.get(token)) === readback;
1751
+ }
1752
+ function hasPlainPrototype(value) {
1753
+ const proto = Object.getPrototypeOf(value);
1754
+ return proto === Object.prototype || proto === null;
1755
+ }
1756
+ function isOrdinaryDate(value) {
1757
+ if (Object.getPrototypeOf(value) !== Date.prototype) return false;
1758
+ try {
1759
+ Date.prototype.getTime.call(value);
1760
+ } catch {
1761
+ return false;
1762
+ }
1763
+ return Reflect.ownKeys(value).length === 0;
1764
+ }
1765
+ function deepEqualJsonValue(a, b) {
1766
+ if (a === b) return true;
1767
+ if (Array.isArray(a)) {
1768
+ if (!Array.isArray(b) || a.length !== b.length) return false;
1769
+ return a.every((item, i) => deepEqualJsonValue(item, b[i]));
1770
+ }
1771
+ if (isPlainObject(a)) {
1772
+ if (!isPlainObject(b)) return false;
1773
+ if (!hasPlainPrototype(a) || !hasPlainPrototype(b)) return false;
1774
+ const keys = Object.keys(a);
1775
+ if (keys.length !== Object.keys(b).length) return false;
1776
+ return keys.every((k) => Object.hasOwn(b, k) && deepEqualJsonValue(a[k], b[k]));
1777
+ }
1778
+ return false;
1779
+ }
1780
+
1781
+ //#endregion
1782
+ //#region src/deployment/secret-redaction/readback-certification.ts
1783
+ function refuseUncertifiedSubtree(value, sourceLiterals, mark, seen = /* @__PURE__ */ new Map()) {
1784
+ if (typeof value === "string") {
1785
+ if (isSingleDynamicReferenceToken(value) && spanNamesResolvableService(value) || sourceLiterals.has(value)) return value;
1786
+ if (value === "") return value;
1787
+ return mark ? POSITION_UNCERTIFIED : "***";
1788
+ }
1789
+ if (value === null || typeof value !== "object") return value;
1790
+ const memo = seen.get(value);
1791
+ if (memo !== void 0) return memo;
1792
+ if (Array.isArray(value)) {
1793
+ const out = [];
1794
+ seen.set(value, out);
1795
+ for (const item of value) out.push(refuseUncertifiedSubtree(item, sourceLiterals, mark, seen));
1796
+ return out;
1797
+ }
1798
+ if (isPlainObject(value) && hasPlainPrototype(value)) {
1799
+ const out = Object.create(null);
1800
+ seen.set(value, out);
1801
+ for (const [k, v] of Object.entries(value)) out[k] = refuseUncertifiedSubtree(v, sourceLiterals, mark, seen);
1802
+ return out;
1803
+ }
1804
+ if (ArrayBuffer.isView(value)) return mark ? POSITION_UNCERTIFIED : "***";
1805
+ seen.set(value, value);
1806
+ return value;
1807
+ }
1808
+ function refuseAgainstSource(bag, source, failClosed, mark) {
1809
+ if (failClosed !== true) return bag;
1810
+ return refuseUncertifiedSubtree(bag, wholeStringLeavesOf(source), mark);
1811
+ }
1812
+ function unpairedSourceCarriesReference(source, key, bagIdentities) {
1813
+ return source.some((item) => !bagIdentities.has(item[key]) && subtreeHasDynamicReference(item));
1814
+ }
1815
+ function refuseUncertifiedReadbackPositions(bag, source, secrets, failClosed, learn, mark) {
1816
+ if (isDynamicReferenceString(source) && typeof bag === "string") {
1817
+ if (isSingleDynamicReferenceToken(source)) {
1818
+ if (learn) learnWholeTokenNeedle(learn, bag, source);
1819
+ return mark ? POSITION_DECIDED : source;
1820
+ }
1821
+ if (mixedLeafMayCarryPublicReference(source, secrets, bag)) return mark ? POSITION_DECIDED : bag;
1822
+ if (learn) learnMixedLeafNeedle(learn, bag, source);
1823
+ return mark ? POSITION_DECIDED : source;
1824
+ }
1825
+ if (!subtreeHasDynamicReference(source)) return bag;
1826
+ if (isPlainObject(bag) && hasPlainPrototype(bag) && isPlainObject(source)) {
1827
+ const out = Object.create(null);
1828
+ for (const [k, v] of Object.entries(bag)) out[k] = Object.hasOwn(source, k) ? refuseUncertifiedReadbackPositions(v, source[k], secrets, failClosed, learn, mark) : v;
1829
+ return out;
1830
+ }
1831
+ if (Array.isArray(bag) && Array.isArray(source)) {
1832
+ const key = identityKeyFor(bag, source);
1833
+ if (key === void 0) {
1834
+ if (!unkeyedArrayPairsByAnchors(bag, source)) return refuseAgainstSource(bag, source, failClosed, mark);
1835
+ return bag.map((item, i) => refuseUncertifiedReadbackPositions(item, source[i], secrets, failClosed, learn, mark));
1836
+ }
1837
+ const sourceByIdentity = /* @__PURE__ */ new Map();
1838
+ for (const item of source) sourceByIdentity.set(item[key], item);
1839
+ const orphanLiterals = unpairedSourceCarriesReference(source, key, new Set(bag.map((item) => item[key]))) && failClosed === true ? wholeStringLeavesOf(source) : void 0;
1840
+ return bag.map((item) => {
1841
+ const partner = sourceByIdentity.get(item[key]);
1842
+ return partner === void 0 ? orphanLiterals !== void 0 ? refuseUncertifiedSubtree(item, orphanLiterals, mark) : item : refuseUncertifiedReadbackPositions(item, partner, secrets, failClosed, learn, mark);
1843
+ });
1844
+ }
1845
+ return refuseAgainstSource(bag, source, failClosed, mark);
1846
+ }
1847
+
1848
+ //#endregion
1849
+ //#region src/deployment/secret-redaction/anchors.ts
1850
+ function isDistinguishingAnchor(value) {
1851
+ if (typeof value === "string") return value !== "";
1852
+ if (Array.isArray(value)) return value.some(isDistinguishingAnchor);
1853
+ if (isPlainObject(value)) return Object.values(value).some(isDistinguishingAnchor);
1854
+ return false;
1855
+ }
1856
+ const ANCHOR_REFERENCE_MASK = "<ref>";
1857
+ function anchorSignature(source) {
1858
+ if (isDynamicReferenceString(source)) return ANCHOR_REFERENCE_MASK;
1859
+ if (Array.isArray(source)) return `[${source.map(anchorSignature).sort().join(",")}]`;
1860
+ if (isPlainObject(source)) return `{${Object.keys(source).sort().map((k) => `${JSON.stringify(k)}:${anchorSignature(source[k])}`).join(",")}}`;
1861
+ return JSON.stringify(source) ?? "undefined";
1862
+ }
1863
+ function anchorsCorroboratePairing(bag, source, anchors) {
1864
+ if (!subtreeHasDynamicReference(source)) {
1865
+ if (!deepEqualJsonValue(bag, source)) return false;
1866
+ if (isDistinguishingAnchor(source)) anchors.distinguishing += 1;
1867
+ return true;
1868
+ }
1869
+ if (isDynamicReferenceString(source)) return typeof bag === "string";
1870
+ if (isPlainObject(source)) {
1871
+ if (!isPlainObject(bag)) return false;
1872
+ return Object.keys(source).every((k) => Object.hasOwn(bag, k) && anchorsCorroboratePairing(bag[k], source[k], anchors));
1873
+ }
1874
+ if (Array.isArray(source)) {
1875
+ if (!Array.isArray(bag) || bag.length !== source.length) return false;
1876
+ return source.every((item, i) => anchorsCorroboratePairing(bag[i], item, anchors));
1877
+ }
1878
+ return false;
1879
+ }
1880
+ function unkeyedArrayPairsByAnchors(bag, source) {
1881
+ if (bag.length !== source.length) return false;
1882
+ const distinguishingPerElement = [];
1883
+ for (const [i, item] of source.entries()) {
1884
+ const anchors = { distinguishing: 0 };
1885
+ if (!anchorsCorroboratePairing(bag[i], item, anchors)) return false;
1886
+ distinguishingPerElement.push(anchors.distinguishing);
1887
+ }
1888
+ const frameDistinguishing = source.reduce((total, item, i) => subtreeHasDynamicReference(item) ? total : total + distinguishingPerElement[i], 0);
1889
+ const signatures = /* @__PURE__ */ new Set();
1890
+ for (const [i, item] of source.entries()) {
1891
+ if (!subtreeHasDynamicReference(item)) continue;
1892
+ const signature = anchorSignature(item);
1893
+ if (signatures.has(signature)) return false;
1894
+ signatures.add(signature);
1895
+ if (distinguishingPerElement[i] > 0) continue;
1896
+ if (!isDynamicReferenceString(item) || frameDistinguishing === 0) return false;
1897
+ }
1898
+ return true;
1899
+ }
1900
+ const POSITION_DECIDED = Symbol("position decided by a position pass");
1901
+ const POSITION_UNCERTIFIED = Symbol("position refused by a position pass");
1902
+ function learnNeedle(collector, plaintext, expression) {
1903
+ if (plaintext.length < 4) return;
1904
+ if (collector.poisoned.has(plaintext)) return;
1905
+ if (expressionSecretIsInferred(expression)) collector.inferred.add(plaintext);
1906
+ const already = collector.needles.get(plaintext);
1907
+ if (already === void 0) {
1908
+ collector.needles.set(plaintext, expression);
1909
+ return;
1910
+ }
1911
+ if (already === expression) return;
1912
+ collector.needles.delete(plaintext);
1913
+ collector.poisoned.add(plaintext);
1914
+ }
1915
+ const SECRET_BEARING_REFERENCE_PREFIXES = [
1916
+ "{{resolve:secretsmanager:",
1917
+ "{{resolve:ssm-secure:",
1918
+ "{{resolve:ssm:"
1919
+ ];
1920
+ function spanNamesResolvableService(token) {
1921
+ return SECRET_BEARING_REFERENCE_PREFIXES.some((prefix) => token.startsWith(prefix));
1922
+ }
1923
+ function resolvableReferenceSpans(value, vouchedExpressions) {
1924
+ const spans = dynamicReferenceSpans(value).filter((span) => spanNamesResolvableService(value.slice(span.start, span.end)));
1925
+ const starts = new Set(spans.map((span) => span.start));
1926
+ for (const prefix of SECRET_BEARING_REFERENCE_PREFIXES) for (let at = value.indexOf(prefix); at >= 0; at = value.indexOf(prefix, at + 1)) {
1927
+ if (starts.has(at)) continue;
1928
+ DYNAMIC_REFERENCE_TOKEN_SCAN.lastIndex = at;
1929
+ const match = DYNAMIC_REFERENCE_TOKEN_SCAN.exec(value);
1930
+ DYNAMIC_REFERENCE_TOKEN_SCAN.lastIndex = 0;
1931
+ if (match?.index !== at) continue;
1932
+ const end = at + match[0].length;
1933
+ if (!vouchedExpressions.has(value.slice(at, end))) continue;
1934
+ spans.push({
1935
+ start: at,
1936
+ end
1937
+ });
1938
+ starts.add(at);
1939
+ }
1940
+ return spans;
1941
+ }
1942
+ const SPELLED_SECRET_REFERENCE_PREFIXES = ["{{resolve:secretsmanager:", "{{resolve:ssm-secure:"];
1943
+ function expressionSecretIsInferred(expression) {
1944
+ return !SPELLED_SECRET_REFERENCE_PREFIXES.some((prefix) => expression.startsWith(prefix));
1945
+ }
1946
+ function expressionMaySeedANeedle(expression) {
1947
+ return isRecordedSecretExpression(expression) || SECRET_BEARING_REFERENCE_PREFIXES.some((prefix) => expression.startsWith(prefix));
1948
+ }
1949
+ function learnWholeTokenNeedle(collector, bag, source) {
1950
+ if (isSingleDynamicReferenceToken(bag)) return;
1951
+ if (!expressionMaySeedANeedle(source)) return;
1952
+ learnNeedle(collector, bag, source);
1953
+ }
1954
+ function learnMixedLeafNeedle(collector, bag, source) {
1955
+ const frame = singleSpanFrame(bag, source);
1956
+ if (frame === void 0) return;
1957
+ const { token, middle: plaintext } = frame;
1958
+ if (!expressionMaySeedANeedle(token)) return;
1959
+ learnNeedle(collector, plaintext, token);
1960
+ }
1961
+ function asChild(marks, key) {
1962
+ return isPlainObject(marks) && hasPlainPrototype(marks) ? marks[key] : void 0;
1963
+ }
1964
+ function asIndex(marks, index) {
1965
+ return Array.isArray(marks) ? marks[index] : void 0;
1966
+ }
1967
+ function preferPositionDecisions(scanned, refused, bag, marks, inferred) {
1968
+ if (isPlainObject(bag) && hasPlainPrototype(bag) && isPlainObject(refused) && isPlainObject(scanned)) {
1969
+ const out = Object.create(null);
1970
+ for (const [k, v] of Object.entries(refused)) out[k] = preferPositionDecisions(scanned[k], v, bag[k], asChild(marks, k), inferred);
1971
+ return out;
1972
+ }
1973
+ if (Array.isArray(bag) && Array.isArray(refused) && Array.isArray(scanned) && refused.length === bag.length && scanned.length === bag.length) return refused.map((item, i) => preferPositionDecisions(scanned[i], item, bag[i], asIndex(marks, i), inferred));
1974
+ if (typeof bag !== "string" || marks === POSITION_DECIDED) return refused;
1975
+ const scanDecision = scanned === bag ? inferred.get(bag) ?? scanned : scanned;
1976
+ if (marks === POSITION_UNCERTIFIED && !(typeof scanDecision === "string" && isSingleDynamicReferenceToken(scanDecision))) return refused;
1977
+ return scanDecision;
1978
+ }
1979
+ function deriveReadbackNeedles(bag, source, secrets, rules) {
1980
+ if (!isReadbackProjectedFromState(rules)) return void 0;
1981
+ if (secrets.size > 0) return void 0;
1982
+ if (!subtreeHasDynamicReference(source)) return void 0;
1983
+ const collector = {
1984
+ needles: /* @__PURE__ */ new Map(),
1985
+ poisoned: /* @__PURE__ */ new Set(),
1986
+ inferred: /* @__PURE__ */ new Set()
1987
+ };
1988
+ refuseUncertifiedReadbackPositions(bag, source, secrets, false, collector);
1989
+ if (collector.needles.size === 0) return void 0;
1990
+ const certain = /* @__PURE__ */ new Map();
1991
+ const inferred = /* @__PURE__ */ new Map();
1992
+ for (const [plaintext, expression] of collector.needles) (collector.inferred.has(plaintext) ? inferred : certain).set(plaintext, expression);
1993
+ return {
1994
+ certain,
1995
+ inferred
1996
+ };
1997
+ }
1998
+
1999
+ //#endregion
2000
+ //#region src/deployment/secret-redaction/redact-state.ts
2001
+ function redactSecretsForState(bag, secrets, source, rules = TEMPLATE_DERIVED_RULES, bagIsSameGeneration = false) {
2002
+ return flattenEmbeddedNoEchoLeaves(redactSecretsForStatePasses(bag, secrets, source, rules, bagIsSameGeneration), secrets);
2003
+ }
2004
+ function redactSecretsForStatePasses(bag, secrets, source, rules, bagIsSameGeneration) {
2005
+ if (secrets.size === 0 && source === void 0) return bag;
2006
+ if (source !== void 0) {
2007
+ const positioned = redactByPath(bag, source, secrets, rules, recordedExpressionsOf(secrets), bagIsSameGeneration);
2008
+ if (!isReadbackProjectedFromState(rules)) return positioned;
2009
+ const failClosed = rules.failClosedOnUncertifiedPositions === true;
2010
+ const refused = refuseUncertifiedReadbackPositions(positioned, source, secrets, failClosed);
2011
+ const derived = deriveReadbackNeedles(bag, source, secrets, rules);
2012
+ if (derived === void 0) return refused;
2013
+ const marks = refuseUncertifiedReadbackPositions(positioned, source, secrets, failClosed, void 0, true);
2014
+ return preferPositionDecisions(redactForState(bag, {
2015
+ provenance: "value-scan",
2016
+ secrets: derived.certain
2017
+ }), refused, bag, marks, derived.inferred);
2018
+ }
2019
+ const regex = buildNeedleRegex(substringNeedlesOf(secrets));
2020
+ const wholeValueExpr = (s) => s === "" ? void 0 : secrets.get(s);
2021
+ const physicalNames = physicalNameCopiesOf.get(secrets);
2022
+ let vouched;
2023
+ const vouchedExpressions = () => vouched ??= new Set(secrets.values());
2024
+ const expressionFor = (match) => {
2025
+ return secrets.get(match) ?? "***";
2026
+ };
2027
+ const scanLeaf = (value, needles) => {
2028
+ const spans = isDynamicReferenceString(value) ? resolvableReferenceSpans(value, vouchedExpressions()) : [];
2029
+ const names = physicalNameSpans(physicalNames, value);
2030
+ const kept = (start, end) => isStrictlyInsideASpan(spans, start, end) || names.some((n) => n.start <= start && end <= n.end);
2031
+ if (!legacyOverlapScan.has(secrets)) {
2032
+ const matches = everyMatch(value, needles);
2033
+ const redacted = matches.filter((m) => !kept(m.start, m.end));
2034
+ if (cutsContainmentNeedle(value, secrets, redacted)) {
2035
+ overlapAnswered.add(secrets);
2036
+ return "***";
2037
+ }
2038
+ const overlapping = redactOverlappingMatches(value, matches, redacted, expressionFor);
2039
+ if (overlapping !== void 0) {
2040
+ overlapAnswered.add(secrets);
2041
+ return overlapping;
2042
+ }
2043
+ }
2044
+ return value.replace(needles, (match, offset) => {
2045
+ if (kept(offset, offset + match.length)) return match;
2046
+ return expressionFor(match);
2047
+ });
2048
+ };
2049
+ const walk = (value) => {
2050
+ if (typeof value === "string") {
2051
+ if (physicalNames?.has(value) === true) return value;
2052
+ const whole = wholeValueExpr(value);
2053
+ if (whole !== void 0) return whole;
2054
+ if (isSingleDynamicReferenceToken(value) && spanNamesResolvableService(value)) return value;
2055
+ if (!regex) return value;
2056
+ regex.lastIndex = 0;
2057
+ if (!regex.test(value)) return value;
2058
+ return scanLeaf(value, regex);
2059
+ }
2060
+ if (Array.isArray(value)) return value.map(walk);
2061
+ if (value !== null && typeof value === "object") {
2062
+ if (isOrdinaryDate(value)) {
2063
+ const persisted = value.toJSON();
2064
+ if (typeof persisted !== "string") return value;
2065
+ const scanned = walk(persisted);
2066
+ return scanned === persisted ? value : scanned;
2067
+ }
2068
+ const out = Object.create(null);
2069
+ for (const [k, v] of Object.entries(value)) out[k] = walk(v);
2070
+ return out;
2071
+ }
2072
+ return value;
2073
+ };
2074
+ return walk(bag);
2075
+ }
2076
+ const legacyOverlapScan = /* @__PURE__ */ new WeakSet();
2077
+ const overlapAnswered = /* @__PURE__ */ new WeakSet();
2078
+ function overlapRuleAnswered(secrets) {
2079
+ return overlapAnswered.has(secrets);
2080
+ }
2081
+ function withLegacyOverlapScan(secrets, run) {
2082
+ if (legacyOverlapScan.has(secrets)) return run();
2083
+ legacyOverlapScan.add(secrets);
2084
+ try {
2085
+ return run();
2086
+ } finally {
2087
+ legacyOverlapScan.delete(secrets);
2088
+ }
2089
+ }
2090
+ function differingStringLeaves(legacy, current, out = []) {
2091
+ if (typeof legacy === "string" && typeof current === "string") {
2092
+ if (legacy !== current) out.push([legacy, current]);
2093
+ return out;
2094
+ }
2095
+ if (Array.isArray(legacy) && Array.isArray(current) && legacy.length === current.length) {
2096
+ legacy.forEach((item, i) => differingStringLeaves(item, current[i], out));
2097
+ return out;
2098
+ }
2099
+ if (legacy !== null && current !== null && typeof legacy === "object" && typeof current === "object" && !Array.isArray(legacy) && !Array.isArray(current)) {
2100
+ const c = current;
2101
+ for (const [key, value] of Object.entries(legacy)) if (Object.hasOwn(c, key)) differingStringLeaves(value, c[key], out);
2102
+ }
2103
+ return out;
2104
+ }
2105
+ function legacyLeavesWithoutMask(legacy) {
2106
+ if (typeof legacy === "string") return legacy.includes("***") ? void 0 : legacy;
2107
+ if (Array.isArray(legacy)) return legacy.map(legacyLeavesWithoutMask);
2108
+ if (legacy !== null && typeof legacy === "object") {
2109
+ const out = Object.create(null);
2110
+ for (const [key, value] of Object.entries(legacy)) out[key] = legacyLeavesWithoutMask(value);
2111
+ return out;
2112
+ }
2113
+ return legacy;
2114
+ }
2115
+ function legacyReferenceLeavesOnly(legacy) {
2116
+ if (typeof legacy === "string") return legacy.includes("{{resolve:") ? legacy : void 0;
2117
+ if (Array.isArray(legacy)) return legacy.map(legacyReferenceLeavesOnly);
2118
+ if (legacy !== null && typeof legacy === "object") {
2119
+ const out = Object.create(null);
2120
+ for (const [key, value] of Object.entries(legacy)) out[key] = legacyReferenceLeavesOnly(value);
2121
+ return out;
2122
+ }
2123
+ return legacy;
2124
+ }
2125
+ function replaceWholeLeaves(value, pairs) {
2126
+ if (pairs.length === 0) return value;
2127
+ if (typeof value === "string") {
2128
+ const hit = pairs.find(([legacy]) => legacy === value);
2129
+ return hit === void 0 ? value : hit[1];
2130
+ }
2131
+ if (Array.isArray(value)) {
2132
+ let changed = false;
2133
+ const out = value.map((item) => {
2134
+ const next = replaceWholeLeaves(item, pairs);
2135
+ if (next !== item) changed = true;
2136
+ return next;
2137
+ });
2138
+ return changed ? out : value;
2139
+ }
2140
+ if (value !== null && typeof value === "object") {
2141
+ let out;
2142
+ for (const [key, item] of Object.entries(value)) {
2143
+ const next = replaceWholeLeaves(item, pairs);
2144
+ if (next !== item) {
2145
+ out ??= Object.assign(Object.create(null), value);
2146
+ out[key] = next;
2147
+ }
2148
+ }
2149
+ return out ?? value;
2150
+ }
2151
+ return value;
2152
+ }
2153
+ function everyMatch(value, needles) {
2154
+ const matches = [];
2155
+ needles.lastIndex = 0;
2156
+ for (let m = needles.exec(value); m !== null; m = needles.exec(value)) {
2157
+ matches.push({
2158
+ start: m.index,
2159
+ end: m.index + m[0].length
2160
+ });
2161
+ needles.lastIndex = m.index + 1;
2162
+ }
2163
+ needles.lastIndex = 0;
2164
+ return matches;
2165
+ }
2166
+ function redactOverlappingMatches(value, matches, redacted, expressionFor) {
2167
+ let reach = 0;
2168
+ let overlap = false;
2169
+ for (const m of matches) {
2170
+ if (m.start < reach) overlap = true;
2171
+ reach = Math.max(reach, m.end);
2172
+ }
2173
+ if (!overlap) return void 0;
2174
+ let out = "";
2175
+ let cursor = 0;
2176
+ for (let i = 0; i < redacted.length;) {
2177
+ let end = redacted[i].end;
2178
+ let j = i + 1;
2179
+ for (; j < redacted.length && redacted[j].start < end; j++) end = Math.max(end, redacted[j].end);
2180
+ const cluster = redacted.slice(i, j);
2181
+ out += value.slice(cursor, cluster[0].start);
2182
+ for (let at = cluster[0].start; at < end;) {
2183
+ const tile = cluster.find((m) => m.start === at);
2184
+ if (tile === void 0) return "***";
2185
+ out += expressionFor(value.slice(tile.start, tile.end));
2186
+ at = tile.end;
2187
+ }
2188
+ cursor = end;
2189
+ i = j;
2190
+ }
2191
+ return out + value.slice(cursor);
2192
+ }
2193
+ function scrubResourceRecord(record, secrets, sourceProperties, observedProvenance, claims = {}) {
2194
+ const propertiesClaimed = claims.properties === "same-generation";
2195
+ const observedClaimed = claims.observed === "same-generation";
2196
+ if (secrets.size === 0 && sourceProperties === void 0 && !record.observedProperties) return record;
2197
+ const next = { ...record };
2198
+ next.properties = sourceProperties === void 0 ? redactForState(record.properties, {
2199
+ provenance: "value-scan",
2200
+ secrets
2201
+ }) : redactAtProvenance(record.properties, secrets, sourceProperties, "template-derived", propertiesClaimed);
2202
+ if (record.attributes) {
2203
+ const ownId = record.physicalId;
2204
+ const ids = [...attributeIdCopiesOf.get(secrets) ?? []];
2205
+ if (typeof ownId === "string" && ownId !== "") ids.push(ownId);
2206
+ const attributes = record.attributes;
2207
+ next.attributes = ids.length > 0 ? withPhysicalNameCopies(secrets, ids, () => redactForState(attributes, {
2208
+ provenance: "value-scan",
2209
+ secrets
2210
+ })) : redactForState(attributes, {
2211
+ provenance: "value-scan",
2212
+ secrets
2213
+ });
2214
+ }
2215
+ if (record.observedProperties) next.observedProperties = redactAtProvenance(record.observedProperties, secrets, sourceProperties ?? next.properties, observedProvenance ?? (sourceProperties !== void 0 ? "template-sourced" : secrets.size === 0 && observedClaimed ? "state-baseline" : "state-readback"), observedClaimed);
2216
+ return next;
2217
+ }
2218
+ function withoutPhysicalNameCopies(secrets, run) {
2219
+ const names = physicalNameCopiesOf.get(secrets);
2220
+ if (names === void 0) return run();
2221
+ physicalNameCopiesOf.delete(secrets);
2222
+ try {
2223
+ return run();
2224
+ } finally {
2225
+ physicalNameCopiesOf.set(secrets, names);
2226
+ }
2227
+ }
2228
+ function withPhysicalNameCopies(secrets, ids, run) {
2229
+ const previous = physicalNameCopiesOf.get(secrets);
2230
+ const names = new Set(previous);
2231
+ for (const id of ids) if (id !== "") names.add(id);
2232
+ physicalNameCopiesOf.set(secrets, names);
2233
+ try {
2234
+ return run();
2235
+ } finally {
2236
+ if (previous === void 0) physicalNameCopiesOf.delete(secrets);
2237
+ else physicalNameCopiesOf.set(secrets, previous);
2238
+ }
2239
+ }
2240
+ function repairPhysicalNameCopyLeaves(stored, legacy, current) {
2241
+ if (typeof stored === "string") return typeof legacy === "string" && typeof current === "string" && stored === legacy && current !== legacy ? current : stored;
2242
+ if (Array.isArray(stored)) {
2243
+ if (!Array.isArray(legacy) || !Array.isArray(current)) return stored;
2244
+ if (legacy.length !== stored.length || current.length !== stored.length) return stored;
2245
+ let changed = false;
2246
+ const out = stored.map((item, i) => {
2247
+ const next = repairPhysicalNameCopyLeaves(item, legacy[i], current[i]);
2248
+ if (next !== item) changed = true;
2249
+ return next;
2250
+ });
2251
+ return changed ? out : stored;
2252
+ }
2253
+ if (stored !== null && typeof stored === "object") {
2254
+ if (legacy === null || typeof legacy !== "object" || Array.isArray(legacy)) return stored;
2255
+ if (current === null || typeof current !== "object" || Array.isArray(current)) return stored;
2256
+ const l = legacy;
2257
+ const c = current;
2258
+ let out;
2259
+ for (const [key, value] of Object.entries(stored)) {
2260
+ if (!Object.hasOwn(l, key) || !Object.hasOwn(c, key)) continue;
2261
+ const next = repairPhysicalNameCopyLeaves(value, l[key], c[key]);
2262
+ if (next !== value) {
2263
+ out ??= { ...stored };
2264
+ out[key] = next;
2265
+ }
2266
+ }
2267
+ return out ?? stored;
2268
+ }
2269
+ return stored;
2270
+ }
2271
+ function embeddedPhysicalNameRewrites(secrets, legacySecrets = secrets) {
2272
+ const names = physicalNameCopiesOf.get(secrets);
2273
+ if (names === void 0) return [];
2274
+ const pairs = [];
2275
+ withoutPhysicalNameCopies(legacySecrets, () => {
2276
+ for (const name of names) {
2277
+ const legacy = redactForState(name, {
2278
+ provenance: "value-scan",
2279
+ secrets: legacySecrets
2280
+ });
2281
+ if (typeof legacy === "string" && legacy !== name && legacy.includes("{{resolve:") && !isSingleDynamicReferenceToken(legacy) && !legacy.includes("***")) pairs.push([legacy, name]);
2282
+ }
2283
+ });
2284
+ return pairs;
2285
+ }
2286
+ function replaceEmbeddedPhysicalNameRewrites(value, pairs) {
2287
+ if (pairs.length === 0) return value;
2288
+ if (typeof value === "string") {
2289
+ let next = value;
2290
+ for (const [legacy, name] of pairs) next = next.split(legacy).join(name);
2291
+ return next;
2292
+ }
2293
+ if (Array.isArray(value)) {
2294
+ let changed = false;
2295
+ const out = value.map((item) => {
2296
+ const next = replaceEmbeddedPhysicalNameRewrites(item, pairs);
2297
+ if (next !== item) changed = true;
2298
+ return next;
2299
+ });
2300
+ return changed ? out : value;
2301
+ }
2302
+ if (value !== null && typeof value === "object") {
2303
+ let out;
2304
+ for (const [key, item] of Object.entries(value)) {
2305
+ const next = replaceEmbeddedPhysicalNameRewrites(item, pairs);
2306
+ if (next !== item) {
2307
+ out ??= { ...value };
2308
+ out[key] = next;
2309
+ }
2310
+ }
2311
+ return out ?? value;
2312
+ }
2313
+ return value;
2314
+ }
2315
+ function repairEmbeddedPhysicalNamesTowards(stored, target, pairs) {
2316
+ if (pairs.length === 0) return stored;
2317
+ if (typeof stored === "string") {
2318
+ if (typeof target !== "string" || stored === target) return stored;
2319
+ const repaired = replaceEmbeddedPhysicalNameRewrites(stored, pairs);
2320
+ return repaired === target ? repaired : stored;
2321
+ }
2322
+ if (Array.isArray(stored)) {
2323
+ if (!Array.isArray(target) || target.length !== stored.length) return stored;
2324
+ let changed = false;
2325
+ const out = stored.map((item, i) => {
2326
+ const next = repairEmbeddedPhysicalNamesTowards(item, target[i], pairs);
2327
+ if (next !== item) changed = true;
2328
+ return next;
2329
+ });
2330
+ return changed ? out : stored;
2331
+ }
2332
+ if (stored !== null && typeof stored === "object") {
2333
+ if (target === null || typeof target !== "object" || Array.isArray(target)) return stored;
2334
+ const t = target;
2335
+ let out;
2336
+ for (const [key, value] of Object.entries(stored)) {
2337
+ if (!Object.hasOwn(t, key)) continue;
2338
+ const next = repairEmbeddedPhysicalNamesTowards(value, t[key], pairs);
2339
+ if (next !== value) {
2340
+ out ??= { ...stored };
2341
+ out[key] = next;
2342
+ }
2343
+ }
2344
+ return out ?? stored;
2345
+ }
2346
+ return stored;
2347
+ }
2348
+
2349
+ //#endregion
2350
+ //#region src/deployment/secret-redaction/mask-errors.ts
2351
+ function maskSecretsInText(text, secrets) {
2352
+ const logOnly = logOnlyValuesOf.get(secrets);
2353
+ const noLogOnly = logOnly === void 0 || logOnly.size === 0;
2354
+ if (secrets.size === 0 && noLogOnly) return text;
2355
+ if (text !== "" && (secrets.has(text) || logOnly?.has(text) === true)) return "***";
2356
+ return maskMatchSpans(text, printingNeedles(noLogOnly ? secrets.keys() : [...secrets.keys(), ...logOnly]), "***");
2357
+ }
2358
+ function maskRecordedSecretsInText(text, secrets) {
2359
+ if (secrets.size === 0) return text;
2360
+ if (text !== "" && secrets.has(text)) return "***";
2361
+ return maskMatchSpans(text, substringNeedles(secrets.keys()), "***");
2362
+ }
2363
+ function substringNeedles(values) {
2364
+ return [...new Set(values)].filter((value) => value.length >= 4);
2365
+ }
2366
+ const ERROR_CAUSE_MASK_MAX_DEPTH = 20;
2367
+ function errorCauseChain(root) {
2368
+ const chain = [];
2369
+ const seen = /* @__PURE__ */ new Set();
2370
+ let current = root;
2371
+ while (current instanceof Error && !seen.has(current) && chain.length < 20) {
2372
+ seen.add(current);
2373
+ chain.push(current);
2374
+ current = current.cause;
2375
+ }
2376
+ return chain;
2377
+ }
2378
+ function readStack(error) {
2379
+ try {
2380
+ const stack = error.stack;
2381
+ return typeof stack === "string" ? stack : void 0;
2382
+ } catch {
2383
+ return;
2384
+ }
2385
+ }
2386
+ function maskDescriptorValue(descriptor, maskText) {
2387
+ if (!("value" in descriptor)) return descriptor;
2388
+ const value = maskErrorFieldValue(descriptor.value, maskText);
2389
+ return value === descriptor.value ? descriptor : {
2390
+ ...descriptor,
2391
+ value
2392
+ };
2393
+ }
2394
+ const CLASSIFIER_IDENTIFIER_FIELDS = /* @__PURE__ */ new Set([
2395
+ "name",
2396
+ "code",
2397
+ "Code",
2398
+ "__type",
2399
+ "ccErrorCode",
2400
+ "ccOperation"
2401
+ ]);
2402
+ function isAuxiliaryAnchor(key, descriptor) {
2403
+ return key === "logicalId" && descriptor.enumerable === false && descriptor.writable === false && typeof descriptor.value === "string" && descriptor.value.endsWith("/auxiliary");
2404
+ }
2405
+ const CREATED_BEFORE_FAILURE_MARK = Symbol.for("cdkd.createdBeforeFailure");
2406
+ function isCreatedBeforeFailureMark(key, descriptor) {
2407
+ return key === CREATED_BEFORE_FAILURE_MARK && descriptor.enumerable === false && descriptor.writable === false && typeof descriptor.value === "object" && descriptor.value !== null && Object.isFrozen(descriptor.value);
2408
+ }
2409
+ function isPlainContainer(value) {
2410
+ const proto = Object.getPrototypeOf(value);
2411
+ return Array.isArray(value) ? proto === Array.prototype : proto === Object.prototype || proto === null;
2412
+ }
2413
+ function readFieldNode(node) {
2414
+ try {
2415
+ if (!isPlainContainer(node)) return void 0;
2416
+ const entries = [];
2417
+ for (const key of Reflect.ownKeys(node)) {
2418
+ const descriptor = Object.getOwnPropertyDescriptor(node, key);
2419
+ if (descriptor) entries.push([key, descriptor]);
2420
+ }
2421
+ const isArray = Array.isArray(node);
2422
+ return {
2423
+ proto: Object.getPrototypeOf(node),
2424
+ isArray,
2425
+ length: isArray ? Object.getOwnPropertyDescriptor(node, "length") : void 0,
2426
+ extensible: Object.isExtensible(node),
2427
+ entries
2428
+ };
2429
+ } catch {
2430
+ return;
2431
+ }
2432
+ }
2433
+ function maskErrorFieldValue(value, maskText) {
2434
+ if (typeof value === "string") return maskText(value);
2435
+ if (typeof value !== "object" || value === null) return value;
2436
+ try {
2437
+ const nodes = /* @__PURE__ */ new Map();
2438
+ const seen = /* @__PURE__ */ new Set();
2439
+ let changed = false;
2440
+ let frontier = [value];
2441
+ for (let depth = 0; frontier.length > 0 && depth < 20; depth++) {
2442
+ const next = [];
2443
+ for (const node of frontier) {
2444
+ if (seen.has(node)) continue;
2445
+ seen.add(node);
2446
+ const read = readFieldNode(node);
2447
+ if (!read) continue;
2448
+ nodes.set(node, read);
2449
+ for (const [, descriptor] of read.entries) {
2450
+ if (!("value" in descriptor)) continue;
2451
+ const child = descriptor.value;
2452
+ if (typeof child === "string") {
2453
+ if (!changed && maskText(child) !== child) changed = true;
2454
+ } else if (typeof child === "object" && child !== null) next.push(child);
2455
+ }
2456
+ }
2457
+ frontier = next;
2458
+ }
2459
+ if (!changed) return value;
2460
+ const copies = /* @__PURE__ */ new Map();
2461
+ for (const [node, read] of nodes) copies.set(node, read.isArray ? new Array(read.length?.value ?? 0) : Object.create(read.proto));
2462
+ for (const [node, read] of nodes) {
2463
+ const copy = copies.get(node);
2464
+ for (const [key, original] of read.entries) {
2465
+ if (key === "length" && read.isArray) continue;
2466
+ const descriptor = { ...original };
2467
+ if ("value" in descriptor) {
2468
+ const child = descriptor.value;
2469
+ if (typeof child === "string") descriptor.value = maskText(child);
2470
+ else if (typeof child === "object" && child !== null && copies.has(child)) descriptor.value = copies.get(child);
2471
+ }
2472
+ Object.defineProperty(copy, key, descriptor);
2473
+ }
2474
+ if (read.length?.writable === false) Object.defineProperty(copy, "length", { writable: false });
2475
+ if (!read.extensible) Object.preventExtensions(copy);
2476
+ }
2477
+ return copies.get(value) ?? value;
2478
+ } catch {
2479
+ return value;
2480
+ }
2481
+ }
2482
+ function maskSecretsInError(error, secrets, extraMask) {
2483
+ if (!(error instanceof Error) || !hasMaskableValues(secrets) && !extraMask) return error;
2484
+ const maskText = (text) => maskSecretsInText(extraMask ? extraMask(text) : text, secrets);
2485
+ const chain = errorCauseChain(error);
2486
+ let changed = false;
2487
+ const links = chain.map((link) => {
2488
+ const message = maskText(link.message);
2489
+ const stack = readStack(link);
2490
+ const maskedStack = typeof stack === "string" ? maskText(stack) : void 0;
2491
+ const descriptors = {};
2492
+ for (const key of Reflect.ownKeys(link)) {
2493
+ if (key === "message" || key === "cause" || key === "stack") continue;
2494
+ const descriptor = Object.getOwnPropertyDescriptor(link, key);
2495
+ if (!descriptor) continue;
2496
+ descriptors[key] = typeof key === "string" && CLASSIFIER_IDENTIFIER_FIELDS.has(key) && typeof descriptor.value === "string" || isAuxiliaryAnchor(key, descriptor) || isCreatedBeforeFailureMark(key, descriptor) ? descriptor : maskDescriptorValue(descriptor, maskText);
2497
+ if (descriptors[key] !== descriptor) changed = true;
2498
+ }
2499
+ let cause = Object.getOwnPropertyDescriptor(link, "cause");
2500
+ if (cause && !("value" in cause && cause.value instanceof Error)) {
2501
+ const masked = maskDescriptorValue(cause, maskText);
2502
+ if (masked !== cause) changed = true;
2503
+ cause = masked;
2504
+ }
2505
+ if (message !== link.message || maskedStack !== stack) changed = true;
2506
+ return {
2507
+ message,
2508
+ stack: maskedStack,
2509
+ descriptors,
2510
+ cause
2511
+ };
2512
+ });
2513
+ if (!changed) return error;
2514
+ const clones = /* @__PURE__ */ new Map();
2515
+ for (const [i, original] of chain.entries()) {
2516
+ const { message, stack, descriptors } = links[i];
2517
+ const clone = Object.create(Object.getPrototypeOf(original), descriptors);
2518
+ Object.defineProperty(clone, "message", {
2519
+ value: message,
2520
+ writable: true,
2521
+ enumerable: false,
2522
+ configurable: true
2523
+ });
2524
+ if (stack !== void 0) Object.defineProperty(clone, "stack", {
2525
+ value: stack,
2526
+ writable: true,
2527
+ enumerable: false,
2528
+ configurable: true
2529
+ });
2530
+ clones.set(original, clone);
2531
+ }
2532
+ for (const [i, original] of chain.entries()) {
2533
+ const causeDescriptor = links[i].cause;
2534
+ if (!causeDescriptor) continue;
2535
+ const clone = clones.get(original);
2536
+ const causeValue = original.cause;
2537
+ const replacement = causeValue instanceof Error ? clones.get(causeValue) : void 0;
2538
+ Object.defineProperty(clone, "cause", replacement ? {
2539
+ value: replacement,
2540
+ writable: true,
2541
+ enumerable: causeDescriptor.enumerable === true,
2542
+ configurable: true
2543
+ } : causeDescriptor);
2544
+ }
2545
+ return clones.get(error);
2546
+ }
2547
+ function createSecretMasker(secrets) {
2548
+ return (text) => maskSecretsInText(text, secrets);
2549
+ }
2550
+
2551
+ //#endregion
2552
+ //#region src/deployment/secret-redaction/log-only.ts
2553
+ const logOnlyValuesOf = /* @__PURE__ */ new WeakMap();
2554
+ function recordLogOnlyValue(secrets, plaintext) {
2555
+ if (plaintext === "") return;
2556
+ sideSetOf(logOnlyValuesOf, secrets).add(plaintext);
2557
+ }
2558
+ function carryLogOnlyValues(from, to) {
2559
+ const values = logOnlyValuesOf.get(from);
2560
+ if (values === void 0 || values.size === 0) return;
2561
+ const into = sideSetOf(logOnlyValuesOf, to);
2562
+ for (const value of values) into.add(value);
2563
+ }
2564
+ function carryLogOnlyValuesCarriedBy(from, to, value) {
2565
+ const values = logOnlyValuesOf.get(from);
2566
+ if (values === void 0 || values.size === 0) return;
2567
+ const leaves = printedLeavesOf(value);
2568
+ if (leaves.size === 0) return;
2569
+ for (const needle of values) for (const leaf of leaves) if (leaf === needle || needle.length >= 4 && leaf.includes(needle)) {
2570
+ recordLogOnlyValue(to, needle);
2571
+ break;
2572
+ }
2573
+ carryListElementFragments(values, to, value);
2574
+ }
2575
+ function carryListElementFragments(needles, to, value) {
2576
+ if (!Array.isArray(value) || value.length === 0) return;
2577
+ if (!value.every((e) => typeof e === "string" || typeof e === "number" || typeof e === "boolean")) return;
2578
+ const numeric = value.every((element) => typeof element === "number");
2579
+ const joined = value.map((element) => String(element)).join(",");
2580
+ for (const needle of [...needles]) {
2581
+ const pieces = needle.split(",").map((piece) => piece.trim());
2582
+ if (numeric && pieces.every((piece) => piece === "" || Number.isNaN(Number(piece)))) continue;
2583
+ const fragments = fragmentsOfNeedleIn(joined, ",", pieces.map((piece) => numeric ? String(Number(piece)) : piece).join(","));
2584
+ if (fragments.size === 0) continue;
2585
+ recordLogOnlyValue(to, needle);
2586
+ for (const fragment of fragments) recordLogOnlyValue(to, fragment);
2587
+ }
2588
+ }
2589
+ function recordLogOnlySplitFragments(from, to, text, delimiter, printed) {
2590
+ if (delimiter === "") return;
2591
+ const splits = [text.split(delimiter)];
2592
+ if (printed !== void 0 && (printed.length !== splits[0].length || printed.some((piece, i) => piece !== splits[0][i]))) splits.push(printed);
2593
+ for (const bag of from) {
2594
+ if (bag === void 0) continue;
2595
+ for (const needle of [...logOnlyValuesOf.get(bag) ?? []]) for (const pieces of splits) for (const fragment of fragmentsOfNeedleIn(text, delimiter, needle, pieces)) if (fragment !== needle) recordLogOnlyValue(to, fragment);
2596
+ }
2597
+ }
2598
+ function fragmentsOfNeedleIn(text, delimiter, needle, pieces = text.split(delimiter)) {
2599
+ const fragments = /* @__PURE__ */ new Set();
2600
+ if (needle === "") return fragments;
2601
+ const starts = [];
2602
+ if (text === needle) starts.push(0);
2603
+ else if (needle.length >= 4) for (let at = text.indexOf(needle); at !== -1; at = text.indexOf(needle, at + 1)) starts.push(at);
2604
+ if (starts.length === 0) return fragments;
2605
+ const spans = [];
2606
+ let offset = 0;
2607
+ for (const piece of pieces) {
2608
+ spans.push([offset, offset + piece.length]);
2609
+ offset += piece.length + delimiter.length;
2610
+ }
2611
+ for (const start of starts) {
2612
+ const end = start + needle.length;
2613
+ for (const [pieceStart, pieceEnd] of spans) {
2614
+ const from = Math.max(pieceStart, start);
2615
+ const to = Math.min(pieceEnd, end);
2616
+ if (from < to) fragments.add(text.slice(from, to));
2617
+ }
2618
+ }
2619
+ return fragments;
2620
+ }
2621
+ function printedLeavesOf(value) {
2622
+ const leaves = wholeStringLeavesOf(value);
2623
+ const seen = /* @__PURE__ */ new Set();
2624
+ const walk = (node) => {
2625
+ if (typeof node === "number" || typeof node === "boolean") {
2626
+ leaves.add(String(node));
2627
+ return;
2628
+ }
2629
+ if (node === null || typeof node !== "object") return;
2630
+ if (seen.has(node)) return;
2631
+ seen.add(node);
2632
+ for (const child of Array.isArray(node) ? node : Object.values(node)) walk(child);
2633
+ };
2634
+ walk(value);
2635
+ return leaves;
2636
+ }
2637
+ function shareLogOnlyValues(view, target) {
2638
+ logOnlyValuesOf.set(view, sideSetOf(logOnlyValuesOf, target));
2639
+ }
2640
+ function recordLogOnlyParameterValue(secrets, value, splitDelimiters) {
2641
+ const record = (leaf) => {
2642
+ const spelled = typeof leaf === "string" ? leaf : typeof leaf === "number" || typeof leaf === "boolean" ? String(leaf) : void 0;
2643
+ if (spelled === void 0) return;
2644
+ recordLogOnlyValue(secrets, spelled);
2645
+ for (const delimiter of splitDelimiters ?? []) {
2646
+ if (delimiter === "" || !spelled.includes(delimiter)) continue;
2647
+ for (const piece of spelled.split(delimiter)) recordLogOnlyValue(secrets, piece);
2648
+ }
2649
+ };
2650
+ if (Array.isArray(value)) {
2651
+ for (const element of value) record(element);
2652
+ record(value.map((element) => String(element)).join(","));
2653
+ return;
2654
+ }
2655
+ record(value);
2656
+ }
2657
+ function literalSplitDelimitersOf(template, parameters) {
2658
+ const delimiters = /* @__PURE__ */ new Set();
2659
+ if (parameters.size === 0) return delimiters;
2660
+ const reads = (node, seen) => {
2661
+ if (typeof node === "string") {
2662
+ for (const name of parameters) if (node.includes(`\${${name}}`)) return true;
2663
+ return false;
2664
+ }
2665
+ if (node === null || typeof node !== "object" || seen.has(node)) return false;
2666
+ seen.add(node);
2667
+ if (!Array.isArray(node)) {
2668
+ const ref = node["Ref"];
2669
+ if (typeof ref === "string" && parameters.has(ref)) return true;
2670
+ }
2671
+ for (const child of Array.isArray(node) ? node : Object.values(node)) if (reads(child, seen)) return true;
2672
+ return false;
2673
+ };
2674
+ const seen = /* @__PURE__ */ new Set();
2675
+ const walk = (node) => {
2676
+ if (node === null || typeof node !== "object" || seen.has(node)) return;
2677
+ seen.add(node);
2678
+ if (Array.isArray(node)) {
2679
+ for (const child of node) walk(child);
2680
+ return;
2681
+ }
2682
+ for (const [key, child] of Object.entries(node)) {
2683
+ if (key === "Fn::Split" && Array.isArray(child) && typeof child[0] === "string" && reads(child[1], /* @__PURE__ */ new Set())) delimiters.add(child[0]);
2684
+ walk(child);
2685
+ }
2686
+ };
2687
+ walk(template);
2688
+ return delimiters;
2689
+ }
2690
+ function printingCorpusOf(secrets) {
2691
+ const logOnly = logOnlyValuesOf.get(secrets);
2692
+ if (logOnly === void 0 || logOnly.size === 0) return secrets;
2693
+ const corpus = new Map(secrets);
2694
+ for (const needle of logOnly) if (!corpus.has(needle)) corpus.set(needle, "***");
2695
+ return corpus;
2696
+ }
2697
+ function logOnlyValueCount(secrets) {
2698
+ return logOnlyValuesOf.get(secrets)?.size ?? 0;
2699
+ }
2700
+ function unionOfSecretBags(bags) {
2701
+ const union = /* @__PURE__ */ new Map();
2702
+ for (const bag of bags) {
2703
+ if (bag === void 0) continue;
2704
+ for (const [plaintext, expression] of bag) if (!union.has(plaintext)) union.set(plaintext, expression);
2705
+ carryLogOnlyValues(bag, union);
2706
+ }
2707
+ return union;
2708
+ }
2709
+ function createUnionSecretMasker(bags) {
2710
+ const present = bags.filter((bag) => bag !== void 0);
2711
+ let stamp;
2712
+ let needles = /* @__PURE__ */ new Set();
2713
+ let scanned = [];
2714
+ return (text) => {
2715
+ const now = present.map((bag) => `${bag.size}:${logOnlyValueCount(bag)}`).join(",");
2716
+ if (now !== stamp) {
2717
+ needles = /* @__PURE__ */ new Set();
2718
+ for (const bag of present) {
2719
+ for (const plaintext of bag.keys()) needles.add(plaintext);
2720
+ for (const plaintext of logOnlyValuesOf.get(bag) ?? []) needles.add(plaintext);
2721
+ }
2722
+ scanned = printingNeedles(needles);
2723
+ stamp = now;
2724
+ }
2725
+ if (text !== "" && needles.has(text)) return "***";
2726
+ return maskMatchSpans(text, scanned, "***");
2727
+ };
2728
+ }
2729
+ function hasLogOnlyValues(secrets) {
2730
+ if (secrets === void 0) return false;
2731
+ const logOnly = logOnlyValuesOf.get(secrets);
2732
+ return logOnly !== void 0 && logOnly.size > 0;
2733
+ }
2734
+ function hasMaskableValues(secrets) {
2735
+ if (secrets === void 0) return false;
2736
+ if (secrets.size > 0) return true;
2737
+ const logOnly = logOnlyValuesOf.get(secrets);
2738
+ return logOnly !== void 0 && logOnly.size > 0;
2739
+ }
2740
+
2741
+ //#endregion
2742
+ //#region src/deployment/secret-redaction/reference-provenance.ts
2743
+ const passProvenanceOf = /* @__PURE__ */ new WeakMap();
2744
+ const MAX_REFERENCE_VERSIONS = 8;
2745
+ function provenanceTableFor(secrets) {
2746
+ let table = passProvenanceOf.get(secrets);
2747
+ if (table === void 0) {
2748
+ table = /* @__PURE__ */ new Map();
2749
+ passProvenanceOf.set(secrets, table);
2750
+ }
2751
+ return table;
2752
+ }
2753
+ function shareReferenceProvenance(view, pass) {
2754
+ passProvenanceOf.set(view, provenanceTableFor(pass));
2755
+ }
2756
+ function entryFor(secrets, token) {
2757
+ const table = provenanceTableFor(secrets);
2758
+ let entry = table.get(token);
2759
+ if (entry === void 0) {
2760
+ entry = {
2761
+ regions: /* @__PURE__ */ new Set(),
2762
+ regionUnknown: false,
2763
+ versions: /* @__PURE__ */ new Set(),
2764
+ versionUnknown: false,
2765
+ readVersions: /* @__PURE__ */ new Set()
2766
+ };
2767
+ table.set(token, entry);
2768
+ }
2769
+ return entry;
2770
+ }
2771
+ function recordReferenceResolution(secrets, token, resolution) {
2772
+ const entry = entryFor(secrets, token);
2773
+ if (resolution.region === void 0) entry.regionUnknown = true;
2774
+ else entry.regions.add(resolution.region);
2775
+ if (resolution.version === void 0) {
2776
+ entry.versionUnknown = true;
2777
+ return;
2778
+ }
2779
+ entry.versions.add(resolution.version);
2780
+ if (resolution.route === "read") entry.readVersions.add(resolution.version);
2781
+ }
2782
+ function recordReferenceProvenanceEntry(secrets, token, provenance, route) {
2783
+ const entry = entryFor(secrets, token);
2784
+ if (provenance.region === void 0) entry.regionUnknown = true;
2785
+ else entry.regions.add(provenance.region);
2786
+ if (provenance.versions === void 0 || provenance.versions.length === 0) {
2787
+ entry.versionUnknown = true;
2788
+ return;
2789
+ }
2790
+ for (const version of provenance.versions) {
2791
+ entry.versions.add(version);
2792
+ if (route === "read") entry.readVersions.add(version);
2793
+ }
2794
+ if (route === "literal") for (const version of provenance.readVersions ?? []) entry.readVersions.add(version);
2795
+ }
2796
+ function mergeReferenceProvenance(from, to) {
2797
+ const source = passProvenanceOf.get(from);
2798
+ if (source === void 0 || from === to) return;
2799
+ for (const [token, entry] of source) {
2800
+ const target = entryFor(to, token);
2801
+ for (const region of entry.regions) target.regions.add(region);
2802
+ target.regionUnknown ||= entry.regionUnknown;
2803
+ for (const version of entry.versions) target.versions.add(version);
2804
+ target.versionUnknown ||= entry.versionUnknown;
2805
+ for (const version of entry.readVersions) target.readVersions.add(version);
2806
+ }
2807
+ }
2808
+ function hasPassProvenance(secrets) {
2809
+ if (secrets === void 0) return false;
2810
+ const table = passProvenanceOf.get(secrets);
2811
+ return table !== void 0 && table.size > 0;
2812
+ }
2813
+ function passProvenanceFor(secrets, token) {
2814
+ if (secrets === void 0) return void 0;
2815
+ const entry = passProvenanceOf.get(secrets)?.get(token);
2816
+ if (entry === void 0) return void 0;
2817
+ return finalize(entry);
2818
+ }
2819
+ function passReadVersionsFor(secrets, token) {
2820
+ if (secrets === void 0) return void 0;
2821
+ const read = passProvenanceOf.get(secrets)?.get(token)?.readVersions;
2822
+ return read === void 0 || read.size === 0 ? void 0 : read;
2823
+ }
2824
+ function finalize(entry) {
2825
+ const out = {};
2826
+ if (!entry.regionUnknown && entry.regions.size === 1) out.region = [...entry.regions][0];
2827
+ if (!entry.versionUnknown && entry.versions.size > 0 && entry.versions.size <= 8) {
2828
+ out.versions = [...entry.versions].sort();
2829
+ if (entry.readVersions.size > 0) out.readVersions = [...entry.readVersions].sort();
2830
+ }
2831
+ return out;
2832
+ }
2833
+ function snapshotPassProvenance(secrets) {
2834
+ if (secrets === void 0) return void 0;
2835
+ const table = passProvenanceOf.get(secrets);
2836
+ if (table === void 0 || table.size === 0) return void 0;
2837
+ const out = /* @__PURE__ */ new Map();
2838
+ for (const [token, entry] of table) out.set(token, finalize(entry));
2839
+ return out;
2840
+ }
2841
+
2842
+ //#endregion
2843
+ //#region src/deployment/secret-redaction/recording.ts
2844
+ const scopeMaps = /* @__PURE__ */ new WeakSet();
2845
+ var WriteThroughSecrets = class extends Map {
2846
+ pass;
2847
+ constructor(pass) {
2848
+ super();
2849
+ this.pass = pass;
2850
+ }
2851
+ set(plaintext, expression) {
2852
+ super.set(plaintext, expression);
2853
+ this.pass.set(plaintext, expression);
2854
+ return this;
2855
+ }
2856
+ };
2857
+ function openRecordingScope(pass, policy) {
2858
+ const secrets = new WriteThroughSecrets(pass);
2859
+ scopeMaps.add(secrets);
2860
+ shareLogOnlyValues(secrets, pass);
2861
+ for (const [kind, share] of Object.entries(SCOPE_SHARERS)) if (policy[kind] === "carry") share(secrets, pass);
2862
+ return {
2863
+ secrets,
2864
+ pass
2865
+ };
2866
+ }
2867
+ const SCOPE_SHARERS = {
2868
+ pairs: sharePairTable,
2869
+ "mask-only-marks": (scope, pass) => {
2870
+ for (const table of [
2871
+ freshNoEchoValuesOf,
2872
+ containmentValuesOf,
2873
+ noEchoParameterValuesOf
2874
+ ]) table.set(scope, sideSetOf(table, pass));
2875
+ },
2876
+ "reference-provenance": shareReferenceProvenance,
2877
+ "physical-name-copies": (scope, pass) => {
2878
+ physicalNameCopiesOf.set(scope, sideSetOf(physicalNameCopiesOf, pass));
2879
+ },
2880
+ "public-proofs": sharePublicProofs
2881
+ };
2882
+ function isRecordingScope(secrets) {
2883
+ return scopeMaps.has(secrets);
2884
+ }
2885
+ function foldRecording(from, to, policy) {
2886
+ if (from === to) return;
2887
+ for (const [plaintext, expression] of from) to.set(plaintext, expression);
2888
+ for (const [kind, carry] of Object.entries(FOLD_CARRIERS)) if (policy[kind] === "carry") carry(from, to);
2889
+ }
2890
+ const FOLD_CARRIERS = {
2891
+ "mask-only-marks": carryMaskOnlyMarks,
2892
+ "reference-provenance": mergeReferenceProvenance,
2893
+ pairs: mergePairTables,
2894
+ "log-only": carryLogOnlyValues,
2895
+ "physical-name-copies": (from, to) => {
2896
+ for (const physicalId of physicalNameCopiesOf.get(from) ?? []) recordPhysicalNameCopy(to, physicalId);
2897
+ },
2898
+ "public-proofs": carryPublicProofs
2899
+ };
2900
+ function recordSecretResolution(secrets, expression, plaintext, options) {
2901
+ secrets.set(plaintext, expression);
2902
+ if (options.pairEvidence) recordResolvedPair(secrets, expression, plaintext);
2903
+ }
2904
+
2905
+ //#endregion
2906
+ //#region src/deployment/secret-redaction/context.ts
2907
+ const RULES_BY_PROVENANCE = {
2908
+ "template-derived": TEMPLATE_DERIVED_RULES,
2909
+ "template-sourced": TEMPLATE_SOURCED_RULES,
2910
+ "state-readback": STATE_SOURCED_READBACK_RULES,
2911
+ "state-baseline": STATE_SOURCED_BASELINE_RULES,
2912
+ "state-cross-generation": STATE_SOURCED_CROSS_GENERATION_RULES,
2913
+ "state-derived": STATE_DERIVED_RULES
2914
+ };
2915
+ function redactForState(bag, context) {
2916
+ refuseScopeAsPass(context.secrets);
2917
+ if (context.provenance === "value-scan") return redactSecretsForState(bag, context.secrets);
2918
+ const { secrets, source, provenance } = context;
2919
+ if (!((context.provenance === "template-derived" || context.provenance === "template-sourced" || context.provenance === "state-derived") && context.generation === "same-generation") || !isPlainBag(bag)) return redactAtProvenance(bag, secrets, source, provenance, false);
2920
+ const copy = shallowCopy(bag);
2921
+ const redacted = redactAtProvenance(copy, secrets, source, provenance, true);
2922
+ return redacted === copy ? shallowCopy(copy) : redacted;
2923
+ }
2924
+ function redactAtProvenance(bag, secrets, source, provenance, sameGeneration) {
2925
+ refuseScopeAsPass(secrets);
2926
+ return redactSecretsForState(bag, secrets, source, RULES_BY_PROVENANCE[provenance], sameGeneration);
2927
+ }
2928
+ function refuseScopeAsPass(secrets) {
2929
+ if (isRecordingScope(secrets)) throw new Error("Internal error: a bag was redacted for persistence with a recording scope; pass the scope's pass map instead.");
2930
+ }
2931
+ function isPlainBag(bag) {
2932
+ if (Array.isArray(bag)) return true;
2933
+ if (bag === null || typeof bag !== "object") return false;
2934
+ const proto = Object.getPrototypeOf(bag);
2935
+ return proto === Object.prototype || proto === null;
2936
+ }
2937
+ function shallowCopy(bag) {
2938
+ return Array.isArray(bag) ? [...bag] : { ...bag };
2939
+ }
2940
+
2941
+ //#endregion
2942
+ //#region src/deployment/secret-redaction/positions.ts
2943
+ const SKELETON_WILDCARD = `${DYNAMIC_REFERENCE_INNER_CHAR}*`;
2944
+ const MAX_SKELETON_WILDCARDS = 3;
2945
+ const MAX_SKELETON_CANDIDATE_LENGTH = 512;
2946
+ function joinSkeletonSegments(segments) {
2947
+ const out = [];
2948
+ let wildcards = 0;
2949
+ for (const segment of segments) {
2950
+ if (segment === "") continue;
2951
+ if (segment === SKELETON_WILDCARD) {
2952
+ if (out[out.length - 1] === SKELETON_WILDCARD) continue;
2953
+ wildcards += 1;
2954
+ if (wildcards > MAX_SKELETON_WILDCARDS) return void 0;
2955
+ }
2956
+ out.push(segment);
2957
+ }
2958
+ return out.join("");
2959
+ }
2960
+ function intrinsicSkeletonPattern(source) {
2961
+ const segments = intrinsicSkeletonSegments(source);
2962
+ if (segments === void 0) return void 0;
2963
+ return anchoredSkeletonPattern(segments);
2964
+ }
2965
+ function anchoredSkeletonPattern(segments) {
2966
+ const body = joinSkeletonSegments(segments.map((segment) => segment === UNKNOWN_PART ? SKELETON_WILDCARD : escapeRegExp(segment)));
2967
+ return body === void 0 ? void 0 : new RegExp(`^${body}$`);
2968
+ }
2969
+ const UNKNOWN_PART = Symbol("unknown intrinsic part");
2970
+ const UNKNOWN_PART_PLACEHOLDER = "\0";
2971
+ function joinPartLiteralText(part) {
2972
+ if (typeof part === "string") return part;
2973
+ if (typeof part === "number" || typeof part === "boolean") return String(part);
2974
+ }
2975
+ function intrinsicSkeletonSegments(source) {
2976
+ const keys = Object.keys(source);
2977
+ if (keys.length !== 1) return void 0;
2978
+ const key = keys[0];
2979
+ if (key === "Fn::Join") {
2980
+ const args = source[key];
2981
+ if (!Array.isArray(args) || args.length !== 2) return void 0;
2982
+ const [delimiter, parts] = args;
2983
+ if (typeof delimiter !== "string" || !Array.isArray(parts)) return void 0;
2984
+ const segments = [];
2985
+ parts.forEach((part, index) => {
2986
+ if (index > 0) segments.push(delimiter);
2987
+ segments.push(joinPartLiteralText(part) ?? UNKNOWN_PART);
2988
+ });
2989
+ return segments;
2990
+ }
2991
+ if (key === "Fn::Sub") {
2992
+ const args = source[key];
2993
+ const template = typeof args === "string" ? args : Array.isArray(args) ? args[0] : void 0;
2994
+ if (typeof template !== "string") return void 0;
2995
+ const segments = [];
2996
+ let cursor = 0;
2997
+ const variable = /\$\{([^}]*)\}/g;
2998
+ let hit;
2999
+ while ((hit = variable.exec(template)) !== null) {
3000
+ segments.push(template.slice(cursor, hit.index));
3001
+ const inner = hit[1];
3002
+ segments.push(inner.startsWith("!") ? `\${${inner.slice(1)}}` : UNKNOWN_PART);
3003
+ cursor = hit.index + hit[0].length;
3004
+ }
3005
+ segments.push(template.slice(cursor));
3006
+ return segments;
3007
+ }
3008
+ }
3009
+ function positionByIntrinsicSkeleton(bag, source, secrets, secretExpressions) {
3010
+ if (bag === "" || !secrets.has(bag)) return void 0;
3011
+ const pattern = intrinsicSkeletonPattern(source);
3012
+ if (!pattern) return void 0;
3013
+ const plaintextOf = plaintextIndexOf(secrets);
3014
+ let matched;
3015
+ for (const candidate of /* @__PURE__ */ new Set([...secretExpressions, ...recordedSecretExpressions])) {
3016
+ if (candidate.length > MAX_SKELETON_CANDIDATE_LENGTH) return void 0;
3017
+ if (!pattern.test(candidate)) continue;
3018
+ const recordedPlaintext = plaintextOf.get(candidate);
3019
+ if (recordedPlaintext !== void 0 && recordedPlaintext !== bag) continue;
3020
+ if (matched !== void 0) return void 0;
3021
+ matched = candidate;
3022
+ }
3023
+ return matched;
3024
+ }
3025
+ function singleSpanFrame(bag, source) {
3026
+ const spans = dynamicReferenceSpans(source);
3027
+ if (spans.length !== 1) return void 0;
3028
+ const [span] = spans;
3029
+ const token = source.slice(span.start, span.end);
3030
+ const prefix = source.slice(0, span.start);
3031
+ const suffix = source.slice(span.end);
3032
+ if (bag.length <= prefix.length + suffix.length) return void 0;
3033
+ if (!bag.startsWith(prefix) || !bag.endsWith(suffix)) return void 0;
3034
+ const middle = bag.slice(prefix.length, bag.length - suffix.length);
3035
+ if (isSingleDynamicReferenceToken(middle)) return void 0;
3036
+ return {
3037
+ token,
3038
+ prefix,
3039
+ suffix,
3040
+ middle
3041
+ };
3042
+ }
3043
+ function positionByEmbeddedSpan(bag, source, secrets, bagIsSameGeneration) {
3044
+ const frame = singleSpanFrame(bag, source);
3045
+ if (frame === void 0) {
3046
+ if (dynamicReferenceSpans(source).length >= 2) {
3047
+ if (certifiesRenderedLiteral(bag, source, secrets, bagIsSameGeneration)) return source;
3048
+ if (bagIsSameGeneration) {
3049
+ const mixed = certifiedMixedLiteral(bag, source, secrets);
3050
+ if (mixed !== void 0) return mixed;
3051
+ }
3052
+ }
3053
+ return redactForState(bag, {
3054
+ provenance: "value-scan",
3055
+ secrets
3056
+ });
3057
+ }
3058
+ const recorded = resolvedPlaintextOf(secrets, frame.token);
3059
+ if (recorded === void 0 || recorded !== frame.middle) return redactForState(bag, {
3060
+ provenance: "value-scan",
3061
+ secrets
3062
+ });
3063
+ return writeFramedTokenWithinScanBound(bag, secrets, frame, bagIsSameGeneration);
3064
+ }
3065
+ function pairedSpans(source, secrets) {
3066
+ const spans = dynamicReferenceSpans(source);
3067
+ if (spans.length === 0) return void 0;
3068
+ const out = [];
3069
+ for (const span of spans) {
3070
+ const plaintext = resolvedPlaintextOf(secrets, source.slice(span.start, span.end));
3071
+ if (plaintext === void 0 || plaintext === "") return void 0;
3072
+ if (isSingleDynamicReferenceToken(plaintext)) return void 0;
3073
+ out.push({
3074
+ ...span,
3075
+ plaintext
3076
+ });
3077
+ }
3078
+ return out;
3079
+ }
3080
+ function renderedTokenOf(spelling, secrets, plaintext) {
3081
+ const spans = pairedSpans(spelling, secrets);
3082
+ if (spans === void 0) return void 0;
3083
+ const tokens = new Set(spans.filter((span) => span.plaintext === plaintext).map((span) => spelling.slice(span.start, span.end)));
3084
+ return tokens.size === 1 ? [...tokens][0] : void 0;
3085
+ }
3086
+ function renderSpans(source, spans, textOf) {
3087
+ let out = "";
3088
+ let cursor = 0;
3089
+ spans.forEach((span, index) => {
3090
+ out += source.slice(cursor, span.start) + textOf(index);
3091
+ cursor = span.end;
3092
+ });
3093
+ return out + source.slice(cursor);
3094
+ }
3095
+ function rendersLiteralTo(spelling, secrets, value) {
3096
+ return renderedLiteralSpans(spelling, secrets, value) !== void 0;
3097
+ }
3098
+ function renderedLiteralSpans(spelling, secrets, value) {
3099
+ const spans = pairedSpans(spelling, secrets);
3100
+ if (spans === void 0) return void 0;
3101
+ if (renderSpans(spelling, spans, (i) => spans[i].plaintext) !== value) return void 0;
3102
+ return redactForState(spelling, {
3103
+ provenance: "value-scan",
3104
+ secrets
3105
+ }) === spelling ? spans : void 0;
3106
+ }
3107
+ function certifiesRenderedLiteral(bag, source, secrets, bagIsSameGeneration) {
3108
+ const spans = renderedLiteralSpans(source, secrets, bag);
3109
+ if (spans === void 0) return false;
3110
+ const survivors = [];
3111
+ for (const span of spans) {
3112
+ const survivor = secrets.get(span.plaintext);
3113
+ if (survivor === void 0) return false;
3114
+ survivors.push(survivor);
3115
+ }
3116
+ const scanned = redactForState(bag, {
3117
+ provenance: "value-scan",
3118
+ secrets
3119
+ });
3120
+ if (scanned === renderSpans(source, spans, (i) => survivors[i])) return true;
3121
+ if (!bagIsSameGeneration) return false;
3122
+ return scanned === renderSpans(source, spans, (i) => spans[i].plaintext.length >= 4 ? survivors[i] : spans[i].plaintext);
3123
+ }
3124
+ function certifiedMixedLiteral(bag, source, secrets) {
3125
+ if (legacyMixedScan.has(secrets)) return void 0;
3126
+ const answer = certifyMixedLiteral(bag, source, secrets);
3127
+ if (answer !== void 0) mixedAnswered.add(secrets);
3128
+ return answer;
3129
+ }
3130
+ const mixedAnswered = /* @__PURE__ */ new WeakSet();
3131
+ const legacyMixedScan = /* @__PURE__ */ new WeakSet();
3132
+ function mixedLiteralAnswered(secrets) {
3133
+ return mixedAnswered.has(secrets);
3134
+ }
3135
+ function withLegacyMixedScan(secrets, run) {
3136
+ if (legacyMixedScan.has(secrets)) return run();
3137
+ legacyMixedScan.add(secrets);
3138
+ try {
3139
+ return run();
3140
+ } finally {
3141
+ legacyMixedScan.delete(secrets);
3142
+ }
3143
+ }
3144
+ function certifyMixedLiteral(bag, source, secrets) {
3145
+ const spans = dynamicReferenceSpans(source);
3146
+ const parts = [];
3147
+ for (const span of spans) {
3148
+ const token = source.slice(span.start, span.end);
3149
+ const plaintext = resolvedPlaintextOf(secrets, token);
3150
+ if (plaintext !== void 0) {
3151
+ if (plaintext === "" || isSingleDynamicReferenceToken(plaintext)) return void 0;
3152
+ const survivor = secrets.get(plaintext);
3153
+ if (survivor === void 0) return void 0;
3154
+ parts.push({
3155
+ kind: "secret",
3156
+ plaintext,
3157
+ survivor
3158
+ });
3159
+ continue;
3160
+ }
3161
+ const value = provenPublicValue(secrets, token);
3162
+ if (value === void 0 || value === "" || value.includes("{{resolve:")) return void 0;
3163
+ parts.push({
3164
+ kind: "public",
3165
+ value
3166
+ });
3167
+ }
3168
+ const secretParts = parts.filter((p) => p.kind === "secret");
3169
+ const publicParts = parts.filter((p) => p.kind === "public");
3170
+ if (secretParts.length === 0 || publicParts.length === 0) return void 0;
3171
+ const textOf = (secret) => (i) => {
3172
+ const part = parts[i];
3173
+ return part.kind === "secret" ? secret(part) : part.value;
3174
+ };
3175
+ if (renderSpans(source, spans, textOf((p) => p.plaintext)) !== bag) return void 0;
3176
+ if (redactForState(source, {
3177
+ provenance: "value-scan",
3178
+ secrets
3179
+ }) !== source) return void 0;
3180
+ if (redactForState(bag, {
3181
+ provenance: "value-scan",
3182
+ secrets
3183
+ }) !== renderSpans(source, spans, textOf((p) => p.plaintext.length >= 4 ? p.survivor : p.plaintext))) return void 0;
3184
+ return renderSpans(source, spans, (i) => {
3185
+ const part = parts[i];
3186
+ return part.kind === "secret" ? source.slice(spans[i].start, spans[i].end) : part.value;
3187
+ });
3188
+ }
3189
+ function writeFramedTokenWithinScanBound(bag, secrets, frame, bagIsSameGeneration) {
3190
+ const scanned = redactForState(bag, {
3191
+ provenance: "value-scan",
3192
+ secrets
3193
+ });
3194
+ const { token, prefix, suffix, middle } = frame;
3195
+ const survivor = secrets.get(middle);
3196
+ if (survivor === void 0) return scanned;
3197
+ if (scanned === prefix + survivor + suffix) return prefix + token + suffix;
3198
+ if (bagIsSameGeneration && scanned === bag) return prefix + token + suffix;
3199
+ return scanned;
3200
+ }
3201
+ function positionByIntrinsicFrame(bag, source, secrets, secretExpressions, bagIsSameGeneration) {
3202
+ const segments = intrinsicSkeletonSegments(source);
3203
+ if (segments === void 0) return void 0;
3204
+ if (segments.some((s) => s !== UNKNOWN_PART && s.includes("\0"))) return;
3205
+ const frame = singleSpanFrame(bag, segments.map((s) => s === UNKNOWN_PART ? "\0" : s).join(""));
3206
+ if (frame === void 0) return void 0;
3207
+ const { token, prefix, suffix, middle } = frame;
3208
+ if (prefix.includes("\0") || suffix.includes("\0")) return;
3209
+ const pattern = anchoredSkeletonPattern(token.split("\0").flatMap((literal, i) => i === 0 ? [literal] : [UNKNOWN_PART, literal]));
3210
+ if (pattern === void 0) return void 0;
3211
+ const plaintextOf = plaintextIndexOf(secrets);
3212
+ let matched;
3213
+ for (const candidate of /* @__PURE__ */ new Set([
3214
+ ...secretExpressions,
3215
+ ...recordedSecretExpressions,
3216
+ ...resolvedExpressionsOf(secrets)
3217
+ ])) {
3218
+ if (candidate.length > MAX_SKELETON_CANDIDATE_LENGTH) return void 0;
3219
+ if (!pattern.test(candidate)) continue;
3220
+ const recordedPlaintext = plaintextOf.get(candidate);
3221
+ if (recordedPlaintext !== void 0 && recordedPlaintext !== middle) continue;
3222
+ if (matched !== void 0) return void 0;
3223
+ matched = candidate;
3224
+ }
3225
+ if (matched === void 0 || resolvedPlaintextOf(secrets, matched) !== middle) return;
3226
+ return writeFramedTokenWithinScanBound(bag, secrets, {
3227
+ token: matched,
3228
+ prefix,
3229
+ suffix,
3230
+ middle
3231
+ }, bagIsSameGeneration);
3232
+ }
3233
+
3234
+ //#endregion
3235
+ //#region src/deployment/secret-redaction/pairs.ts
3236
+ const resolvedPairsOf = /* @__PURE__ */ new WeakMap();
3237
+ const CONFLICTING_PLAINTEXT = Symbol("conflicting plaintext");
3238
+ function pairsFor(secrets) {
3239
+ let pairs = resolvedPairsOf.get(secrets);
3240
+ if (pairs === void 0) {
3241
+ pairs = /* @__PURE__ */ new Map();
3242
+ resolvedPairsOf.set(secrets, pairs);
3243
+ }
3244
+ return pairs;
3245
+ }
3246
+ function recordResolvedPair(secrets, expression, plaintext) {
3247
+ const pairs = pairsFor(secrets);
3248
+ const previous = pairs.get(expression);
3249
+ if (previous === void 0) pairs.set(expression, plaintext);
3250
+ else if (previous !== plaintext) pairs.set(expression, CONFLICTING_PLAINTEXT);
3251
+ }
3252
+ function mergeResolvedPairs(from, to) {
3253
+ carryMaskOnlyMarks(from, to);
3254
+ mergeReferenceProvenance(from, to);
3255
+ mergePairTables(from, to);
3256
+ }
3257
+ function mergePairTables(from, to) {
3258
+ const pairs = resolvedPairsOf.get(from);
3259
+ if (pairs === void 0) return;
3260
+ for (const [expression, plaintext] of pairs) if (typeof plaintext === "string") recordResolvedPair(to, expression, plaintext);
3261
+ else pairsFor(to).set(expression, CONFLICTING_PLAINTEXT);
3262
+ }
3263
+ function sharePairTable(view, pass) {
3264
+ resolvedPairsOf.set(view, pairsFor(pass));
3265
+ }
3266
+ function resolvedPlaintextOf(secrets, expression) {
3267
+ const recorded = resolvedPairsOf.get(secrets)?.get(expression);
3268
+ return typeof recorded === "string" ? recorded : void 0;
3269
+ }
3270
+ function resolvedExpressionsOf(secrets) {
3271
+ return [...resolvedPairsOf.get(secrets)?.keys() ?? []];
3272
+ }
3273
+ const CONFLICTING_LEAF_RESOLUTION = Symbol("conflicting leaf resolution");
3274
+ const intrinsicLeafResolutionsOf = /* @__PURE__ */ new WeakMap();
3275
+ function recordIntrinsicLeafResolution(secrets, source, resolution) {
3276
+ let leaves = intrinsicLeafResolutionsOf.get(secrets);
3277
+ if (leaves === void 0) {
3278
+ leaves = /* @__PURE__ */ new WeakMap();
3279
+ intrinsicLeafResolutionsOf.set(secrets, leaves);
3280
+ }
3281
+ const copy = {
3282
+ input: resolution.input,
3283
+ output: resolution.output,
3284
+ substitutions: resolution.substitutions.map(({ token, value, secret }) => ({
3285
+ token,
3286
+ value,
3287
+ secret
3288
+ })),
3289
+ complete: resolution.complete,
3290
+ ...resolution.parameterSpans === void 0 ? {} : { parameterSpans: resolution.parameterSpans.map(({ start, length, key }) => ({
3291
+ start,
3292
+ length,
3293
+ key
3294
+ })) }
3295
+ };
3296
+ const previous = leaves.get(source);
3297
+ if (previous === void 0) leaves.set(source, copy);
3298
+ else if (previous === CONFLICTING_LEAF_RESOLUTION || !sameLeafResolution(previous, copy)) leaves.set(source, CONFLICTING_LEAF_RESOLUTION);
3299
+ }
3300
+ function intrinsicLeafResolutionOf(secrets, source) {
3301
+ const resolution = intrinsicLeafResolutionsOf.get(secrets)?.get(source);
3302
+ return resolution === CONFLICTING_LEAF_RESOLUTION ? void 0 : resolution;
3303
+ }
3304
+ function recordIntrinsicLeafResolutionAs(secrets, source, branch) {
3305
+ let leaves = intrinsicLeafResolutionsOf.get(secrets);
3306
+ const resolution = typeof branch === "object" && branch !== null ? leaves?.get(branch) : void 0;
3307
+ if (resolution === void 0 || resolution === CONFLICTING_LEAF_RESOLUTION) {
3308
+ if (leaves === void 0) {
3309
+ leaves = /* @__PURE__ */ new WeakMap();
3310
+ intrinsicLeafResolutionsOf.set(secrets, leaves);
3311
+ }
3312
+ leaves.set(source, CONFLICTING_LEAF_RESOLUTION);
3313
+ return;
3314
+ }
3315
+ recordIntrinsicLeafResolution(secrets, source, resolution);
3316
+ }
3317
+ function sameLeafResolution(a, b) {
3318
+ return a.input === b.input && a.output === b.output && a.complete === b.complete && a.substitutions.length === b.substitutions.length && a.substitutions.every((s, i) => s.token === b.substitutions[i].token && s.value === b.substitutions[i].value && s.secret === b.substitutions[i].secret) && sameParameterSpans(a.parameterSpans, b.parameterSpans);
3319
+ }
3320
+ function sameParameterSpans(a, b) {
3321
+ if (a === void 0 || b === void 0) return a === b;
3322
+ return a.length === b.length && a.every((s, i) => s.start === b[i].start && s.length === b[i].length && s.key === b[i].key);
3323
+ }
3324
+ function substitutedSpellingOf(secrets, source, resolvedValue) {
3325
+ const resolution = intrinsicLeafResolutionsOf.get(secrets)?.get(source);
3326
+ if (resolution === void 0 || resolution === CONFLICTING_LEAF_RESOLUTION) return void 0;
3327
+ if (!resolution.complete || resolution.output !== resolvedValue) return void 0;
3328
+ if (resolution.substitutions.length !== 1) return void 0;
3329
+ const [substitution] = resolution.substitutions;
3330
+ if (!substitution.secret) return void 0;
3331
+ const frame = singleSpanFrame(resolvedValue, resolution.input);
3332
+ if (frame === void 0) return void 0;
3333
+ if (frame.token !== substitution.token || frame.middle !== substitution.value) return void 0;
3334
+ for (const plaintext of secrets.keys()) {
3335
+ if (plaintext === "") continue;
3336
+ const outsideToken = frame.prefix.includes(plaintext) || frame.suffix.includes(plaintext);
3337
+ if (plaintext === substitution.value ? outsideToken : resolution.input.includes(plaintext)) return;
3338
+ }
3339
+ return resolution.input;
3340
+ }
3341
+ const sameGenerationBags = /* @__PURE__ */ new WeakSet();
3342
+ function markSameGenerationBag(bag) {
3343
+ sameGenerationBags.add(bag);
3344
+ return bag;
3345
+ }
3346
+ function isSameGenerationBag(bag) {
3347
+ return sameGenerationBags.has(bag);
3348
+ }
3349
+ const leafCertifiedBags = /* @__PURE__ */ new WeakSet();
3350
+ function markLeafCertifiedBag(bag) {
3351
+ leafCertifiedBags.add(bag);
3352
+ return bag;
3353
+ }
3354
+ function isLeafCertifiedBag(bag) {
3355
+ return leafCertifiedBags.has(bag);
3356
+ }
3357
+ function redactionClaimOf(bag) {
3358
+ return isSameGenerationBag(bag) || isLeafCertifiedBag(bag) ? "same-generation" : "unproven";
3359
+ }
3360
+ function carryRedactionMarks(from, to) {
3361
+ if (isSameGenerationBag(from)) markSameGenerationBag(to);
3362
+ if (isLeafCertifiedBag(from)) markLeafCertifiedBag(to);
3363
+ return to;
3364
+ }
3365
+
3366
+ //#endregion
3367
+ //#region src/deployment/secret-redaction/unresolved-token-frame.ts
3368
+ function liveMatchesUnresolvedTokenFrame(send, live, unresolvedTokens, secrets) {
3369
+ const wildcards = dynamicReferenceSpans(send).filter((span) => unresolvedTokens.has(send.slice(span.start, span.end)));
3370
+ if (wildcards.length === 0) return false;
3371
+ if (wildcardOverlapsResolvedPlaintext(send, wildcards, secrets)) return false;
3372
+ const literals = [];
3373
+ let at = 0;
3374
+ for (const span of wildcards) {
3375
+ literals.push(send.slice(at, span.start));
3376
+ at = span.end;
3377
+ }
3378
+ literals.push(send.slice(at));
3379
+ const first = literals[0];
3380
+ const last = literals[literals.length - 1];
3381
+ if (live.length < first.length + last.length) return false;
3382
+ if (!live.startsWith(first) || !live.endsWith(last)) return false;
3383
+ let pos = first.length;
3384
+ const end = live.length - last.length;
3385
+ for (let i = 1; i < literals.length - 1; i++) {
3386
+ const literal = literals[i];
3387
+ const found = live.indexOf(literal, pos);
3388
+ if (found < 0 || found + literal.length > end) return false;
3389
+ pos = found + literal.length;
3390
+ }
3391
+ return true;
3392
+ }
3393
+ function wildcardOverlapsResolvedPlaintext(send, wildcards, secrets) {
3394
+ for (const plaintext of secrets.keys()) {
3395
+ if (plaintext === "") continue;
3396
+ for (let from = send.indexOf(plaintext); from >= 0; from = send.indexOf(plaintext, from + 1)) {
3397
+ const to = from + plaintext.length;
3398
+ if (wildcards.some((span) => span.start < to && from < span.end)) return true;
3399
+ }
3400
+ }
3401
+ return false;
3402
+ }
3403
+
3404
+ //#endregion
3405
+ //#region src/deployment/secret-redaction/noecho-leaves.ts
3406
+ const SUB_VARIABLE = /\$\{([^}!][^}]*)\}/g;
3407
+ function intrinsicKeyOf(node) {
3408
+ const keys = Object.keys(node);
3409
+ if (keys.length !== 1) return void 0;
3410
+ const key = keys[0];
3411
+ return key === "Ref" || key.startsWith("Fn::") ? key : void 0;
3412
+ }
3413
+ function conditionVerdict(sources, name) {
3414
+ if (typeof name !== "string" || sources.conditions === void 0) return void 0;
3415
+ return Object.hasOwn(sources.conditions, name) ? sources.conditions[name] : void 0;
3416
+ }
3417
+ function getAttReadsNoEcho(argument, sources) {
3418
+ if (sources.attributeIsNoEcho === void 0) return false;
3419
+ let target;
3420
+ let attribute;
3421
+ if (typeof argument === "string") {
3422
+ const dot = argument.indexOf(".");
3423
+ if (dot <= 0) return false;
3424
+ target = argument.slice(0, dot);
3425
+ attribute = argument.slice(dot + 1);
3426
+ } else if (Array.isArray(argument) && argument.length === 2) [target, attribute] = argument;
3427
+ else return false;
3428
+ if (typeof target !== "string") return false;
3429
+ if (typeof attribute !== "string") return readsNoEchoSource(attribute, sources);
3430
+ return sources.attributeIsNoEcho(target, attribute);
3431
+ }
3432
+ function subStringReadsNoEcho(text, variables, sources) {
3433
+ for (const match of text.matchAll(SUB_VARIABLE)) {
3434
+ const name = match[1].trim();
3435
+ if (variables !== void 0 && Object.hasOwn(variables, name)) continue;
3436
+ if (sources.parameters.has(name)) return true;
3437
+ const dot = name.indexOf(".");
3438
+ if (dot > 0 && sources.attributeIsNoEcho?.(name.slice(0, dot), name.slice(dot + 1)) === true) return true;
3439
+ }
3440
+ return false;
3441
+ }
3442
+ function readsNoEchoSource(node, sources) {
3443
+ const seen = /* @__PURE__ */ new Set();
3444
+ const walk = (value) => {
3445
+ if (value === null || typeof value !== "object") return false;
3446
+ if (seen.has(value)) return false;
3447
+ seen.add(value);
3448
+ if (Array.isArray(value)) return value.some(walk);
3449
+ const record = value;
3450
+ const intrinsic = intrinsicKeyOf(record);
3451
+ if (intrinsic === "Ref") return typeof record["Ref"] === "string" && sources.parameters.has(record["Ref"]);
3452
+ if (intrinsic === "Fn::GetAtt") return getAttReadsNoEcho(record["Fn::GetAtt"], sources);
3453
+ if (intrinsic === "Fn::Sub") {
3454
+ const argument = record["Fn::Sub"];
3455
+ if (typeof argument === "string") return subStringReadsNoEcho(argument, void 0, sources);
3456
+ if (Array.isArray(argument)) {
3457
+ const [text, variables] = argument;
3458
+ const vars = isPlainObject(variables) ? variables : void 0;
3459
+ if (typeof text === "string" && subStringReadsNoEcho(text, vars, sources)) return true;
3460
+ return vars !== void 0 && Object.values(vars).some(walk);
3461
+ }
3462
+ return walk(argument);
3463
+ }
3464
+ if (intrinsic === "Fn::If") {
3465
+ const argument = record["Fn::If"];
3466
+ if (Array.isArray(argument) && argument.length === 3) {
3467
+ const verdict = conditionVerdict(sources, argument[0]);
3468
+ if (verdict !== void 0) return walk(verdict ? argument[1] : argument[2]);
3469
+ return walk(argument[1]) || walk(argument[2]);
3470
+ }
3471
+ return walk(argument);
3472
+ }
3473
+ return Object.values(record).some(walk);
3474
+ };
3475
+ return walk(node);
3476
+ }
3477
+ function literalIndex(index) {
3478
+ if (typeof index === "number" && Number.isInteger(index)) return index;
3479
+ if (typeof index === "string" && /^\d+$/.test(index)) return Number(index);
3480
+ }
3481
+ function noEchoCoordinatesOf(template, resolved, sources, onCoordinate) {
3482
+ const coordinates = [];
3483
+ const push = (path, node) => {
3484
+ coordinates.push(path);
3485
+ onCoordinate?.(path, node);
3486
+ };
3487
+ const ancestors = /* @__PURE__ */ new Set();
3488
+ const parametersOnly = {
3489
+ parameters: sources.parameters,
3490
+ ...sources.conditions !== void 0 && { conditions: sources.conditions }
3491
+ };
3492
+ const isBareDeclaredGetAtt = (node) => isPlainObject(node) && intrinsicKeyOf(node) === "Fn::GetAtt" && getAttReadsNoEcho(node["Fn::GetAtt"], sources);
3493
+ const walk = (source, value, path) => {
3494
+ if (value === void 0) return;
3495
+ if (source === null || typeof source !== "object") return;
3496
+ if (ancestors.has(source)) return;
3497
+ ancestors.add(source);
3498
+ try {
3499
+ if (Array.isArray(source)) {
3500
+ if (!readsNoEchoSource(source, sources)) return;
3501
+ if (Array.isArray(value) && value.length === source.length) source.forEach((item, index) => walk(item, value[index], [...path, index]));
3502
+ else if (readsNoEchoSource(source, parametersOnly) || source.some(isBareDeclaredGetAtt)) push(path, source);
3503
+ return;
3504
+ }
3505
+ const record = source;
3506
+ const intrinsic = intrinsicKeyOf(record);
3507
+ if (intrinsic === void 0) {
3508
+ if (!isPlainObject(value)) {
3509
+ if (readsNoEchoSource(record, parametersOnly)) push(path, source);
3510
+ return;
3511
+ }
3512
+ for (const [key, child] of Object.entries(record)) if (Object.hasOwn(value, key)) walk(child, value[key], [...path, key]);
3513
+ return;
3514
+ }
3515
+ if (intrinsic === "Fn::If") {
3516
+ const argument = record["Fn::If"];
3517
+ if (Array.isArray(argument) && argument.length === 3) {
3518
+ const verdict = conditionVerdict(sources, argument[0]);
3519
+ if (verdict !== void 0) {
3520
+ walk(verdict ? argument[1] : argument[2], value, path);
3521
+ return;
3522
+ }
3523
+ if (isBareDeclaredGetAtt(argument[1]) || isBareDeclaredGetAtt(argument[2])) {
3524
+ push(path, source);
3525
+ return;
3526
+ }
3527
+ }
3528
+ }
3529
+ if (intrinsic === "Fn::Select") {
3530
+ const argument = record["Fn::Select"];
3531
+ if (Array.isArray(argument) && argument.length === 2 && Array.isArray(argument[1])) {
3532
+ const index = literalIndex(argument[0]);
3533
+ const list = argument[1];
3534
+ if (index !== void 0 && index < list.length) {
3535
+ walk(list[index], value, path);
3536
+ return;
3537
+ }
3538
+ }
3539
+ }
3540
+ if (intrinsic === "Fn::GetAtt") {
3541
+ if (getAttReadsNoEcho(record["Fn::GetAtt"], sources)) push(path, source);
3542
+ return;
3543
+ }
3544
+ if (readsNoEchoSource(record, parametersOnly)) push(path, source);
3545
+ } finally {
3546
+ ancestors.delete(source);
3547
+ }
3548
+ };
3549
+ walk(template, resolved, []);
3550
+ return coordinates;
3551
+ }
3552
+ function noEchoParameterNodesAt(templateProperties, shape, noEchoParameters, conditions) {
3553
+ const nodes = /* @__PURE__ */ new Map();
3554
+ noEchoCoordinatesOf(templateProperties ?? {}, shape, {
3555
+ parameters: noEchoParameters,
3556
+ ...conditions !== void 0 && { conditions }
3557
+ }, (coordinate, node) => nodes.set(canonicalJson(coordinate), {
3558
+ coordinate,
3559
+ node
3560
+ }));
3561
+ return nodes;
3562
+ }
3563
+ function recordedNoEchoValuesOf(templateProperties, record, noEchoParameterNames, conditions) {
3564
+ const bag = /* @__PURE__ */ new Map();
3565
+ const properties = record?.properties;
3566
+ if (properties === null || typeof properties !== "object") return bag;
3567
+ const coordinates = /* @__PURE__ */ new Map();
3568
+ if (noEchoParameterNames.size > 0 && templateProperties !== void 0) for (const [key, { coordinate }] of noEchoParameterNodesAt(templateProperties, properties, noEchoParameterNames, conditions)) coordinates.set(key, coordinate);
3569
+ for (const coordinate of noEchoLeavesOf(record ?? {}) ?? []) coordinates.set(canonicalJson(coordinate), coordinate);
3570
+ const recordLeaf = (leaf) => {
3571
+ if (typeof leaf === "string") {
3572
+ if (leaf === "***" || leaf.includes("{{resolve:")) return;
3573
+ recordLogOnlyValue(bag, leaf);
3574
+ } else if (typeof leaf === "number") recordLogOnlyValue(bag, String(leaf));
3575
+ };
3576
+ const seen = /* @__PURE__ */ new Set();
3577
+ const walk = (value) => {
3578
+ if (value === null || typeof value !== "object") {
3579
+ recordLeaf(value);
3580
+ return;
3581
+ }
3582
+ if (seen.has(value)) return;
3583
+ seen.add(value);
3584
+ for (const child of Object.values(value)) walk(child);
3585
+ };
3586
+ for (const coordinate of coordinates.values()) walk(valueAtCoordinate(properties, coordinate));
3587
+ return bag;
3588
+ }
3589
+ function maskWholeValue(value) {
3590
+ if (value === null || value === void 0) return value;
3591
+ if (typeof value === "string" && isSingleDynamicReferenceToken(value)) return value;
3592
+ if (Array.isArray(value)) return value.map(maskWholeValue);
3593
+ if (isPlainObject(value)) {
3594
+ const out = {};
3595
+ for (const [key, child] of Object.entries(value)) Object.defineProperty(out, key, {
3596
+ value: maskWholeValue(child),
3597
+ enumerable: true,
3598
+ writable: true,
3599
+ configurable: true
3600
+ });
3601
+ return out;
3602
+ }
3603
+ return "***";
3604
+ }
3605
+ function readAt(bag, coordinate) {
3606
+ let node = bag;
3607
+ for (const segment of coordinate) {
3608
+ if (typeof segment === "number") {
3609
+ if (!Array.isArray(node) || segment >= node.length) return {
3610
+ found: false,
3611
+ value: void 0
3612
+ };
3613
+ node = node[segment];
3614
+ continue;
3615
+ }
3616
+ if (!isPlainObject(node) || !Object.hasOwn(node, segment)) return {
3617
+ found: false,
3618
+ value: void 0
3619
+ };
3620
+ node = node[segment];
3621
+ }
3622
+ return {
3623
+ found: true,
3624
+ value: node
3625
+ };
3626
+ }
3627
+ function valueAtCoordinate(bag, coordinate) {
3628
+ return readAt(bag, coordinate).value;
3629
+ }
3630
+ function cloneContainer(node) {
3631
+ if (Array.isArray(node)) return [...node];
3632
+ if (isPlainObject(node)) return { ...node };
3633
+ return node;
3634
+ }
3635
+ function maskAtCoordinates(bag, coordinates) {
3636
+ if (coordinates.length === 0) return bag;
3637
+ let root = bag;
3638
+ for (const coordinate of coordinates) {
3639
+ if (!readAt(root, coordinate).found) continue;
3640
+ if (coordinate.length === 0) {
3641
+ root = maskWholeValue(root);
3642
+ continue;
3643
+ }
3644
+ root = cloneContainer(root);
3645
+ let node = root;
3646
+ for (let i = 0; i < coordinate.length - 1; i++) {
3647
+ const segment = coordinate[i];
3648
+ const next = cloneContainer(node[segment]);
3649
+ node[segment] = next;
3650
+ node = next;
3651
+ }
3652
+ const last = coordinate[coordinate.length - 1];
3653
+ node[last] = maskWholeValue(node[last]);
3654
+ }
3655
+ return root;
3656
+ }
3657
+ function replaceAtCoordinates(bag, entries) {
3658
+ let root = bag;
3659
+ for (const { coordinate, value } of entries) {
3660
+ if (!readAt(root, coordinate).found) continue;
3661
+ if (coordinate.length === 0) {
3662
+ root = value;
3663
+ continue;
3664
+ }
3665
+ root = cloneContainer(root);
3666
+ let node = root;
3667
+ for (let i = 0; i <= coordinate.length - 1; i++) {
3668
+ const segment = coordinate[i];
3669
+ const next = i === coordinate.length - 1 ? value : cloneContainer(node[segment]);
3670
+ if (typeof segment === "number") node[segment] = next;
3671
+ else Object.defineProperty(node, segment, {
3672
+ value: next,
3673
+ enumerable: true,
3674
+ writable: true,
3675
+ configurable: true
3676
+ });
3677
+ node = next;
3678
+ }
3679
+ }
3680
+ return root;
3681
+ }
3682
+ function maskReadbackAtCoordinates(readback, properties, coordinates) {
3683
+ let result = readback;
3684
+ for (const coordinate of coordinates) {
3685
+ const target = readbackPathFor(result, properties, coordinate);
3686
+ if (target !== void 0) result = maskAtCoordinates(result, [target]);
3687
+ }
3688
+ return result;
3689
+ }
3690
+ function readbackPathFor(readback, properties, coordinate) {
3691
+ const path = [];
3692
+ let live = readback;
3693
+ let desired = properties;
3694
+ for (const segment of coordinate) {
3695
+ if (typeof segment === "number") {
3696
+ if (!Array.isArray(live)) return path.length === 0 ? void 0 : path;
3697
+ const key = Array.isArray(desired) ? identityKeyFor(live, desired) : void 0;
3698
+ const element = Array.isArray(desired) ? desired[segment] : void 0;
3699
+ if (key === void 0 || !isPlainObject(element)) return path;
3700
+ const identity = element[key];
3701
+ const index = live.findIndex((item) => isPlainObject(item) && item[key] === identity);
3702
+ if (index < 0) return path;
3703
+ path.push(index);
3704
+ live = live[index];
3705
+ desired = element;
3706
+ continue;
3707
+ }
3708
+ if (!isPlainObject(live)) return path.length === 0 ? void 0 : path;
3709
+ if (!Object.hasOwn(live, segment)) return void 0;
3710
+ path.push(segment);
3711
+ live = live[segment];
3712
+ desired = isPlainObject(desired) ? desired[segment] : void 0;
3713
+ }
3714
+ return path;
3715
+ }
3716
+ function canonicalCoordinates(coordinates) {
3717
+ const keyed = /* @__PURE__ */ new Map();
3718
+ for (const coordinate of coordinates) keyed.set(JSON.stringify(coordinate), [...coordinate]);
3719
+ const all = [...keyed.values()];
3720
+ const covered = (inner) => all.some((outer) => outer.length < inner.length && outer.every((segment, i) => segment === inner[i]));
3721
+ return all.filter((coordinate) => !covered(coordinate)).sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)));
3722
+ }
3723
+ function noEchoLeavesOf(record) {
3724
+ const field = record.noEchoLeaves;
3725
+ if (!Array.isArray(field)) return void 0;
3726
+ const out = [];
3727
+ for (const entry of field) {
3728
+ if (!Array.isArray(entry)) return void 0;
3729
+ if (!entry.every((s) => typeof s === "string" || typeof s === "number" && Number.isInteger(s))) return void 0;
3730
+ out.push(entry);
3731
+ }
3732
+ return out;
3733
+ }
3734
+ function isMarkedCoordinate(coordinate, marked) {
3735
+ return marked.some((outer) => outer.length <= coordinate.length && outer.every((segment, i) => segment === coordinate[i]));
3736
+ }
3737
+ function witnessNormalize(stored, today, v11) {
3738
+ const confirmed = [];
3739
+ const differing = [];
3740
+ const walk = (s, t, v, path) => {
3741
+ if (v !== "***" && containsMask(v) && (Array.isArray(v) ? !Array.isArray(s) || s.length !== v.length : isPlainObject(v) ? !isPlainObject(s) : false)) {
3742
+ if (s === void 0 || containsMask(s)) return s;
3743
+ if (jsonEqual(s, t)) {
3744
+ confirmed.push(path);
3745
+ return v;
3746
+ }
3747
+ differing.push(path);
3748
+ return s;
3749
+ }
3750
+ if (v === "***") {
3751
+ if (s === void 0 || containsMask(s)) return s;
3752
+ if (jsonEqual(s, t)) {
3753
+ confirmed.push(path);
3754
+ return v;
3755
+ }
3756
+ differing.push(path);
3757
+ return s;
3758
+ }
3759
+ if (Array.isArray(v) && Array.isArray(s)) {
3760
+ if (s.length !== v.length) return s;
3761
+ const tt = Array.isArray(t) ? t : [];
3762
+ let changed = false;
3763
+ const out = s.map((item, i) => {
3764
+ const next = walk(item, tt[i], v[i], [...path, i]);
3765
+ if (next !== item) changed = true;
3766
+ return next;
3767
+ });
3768
+ return changed ? out : s;
3769
+ }
3770
+ if (isPlainObject(v) && isPlainObject(s)) {
3771
+ const tt = isPlainObject(t) ? t : {};
3772
+ let out;
3773
+ for (const key of Object.keys(v)) {
3774
+ if (!Object.hasOwn(s, key)) continue;
3775
+ const next = walk(s[key], tt[key], v[key], [...path, key]);
3776
+ if (next !== s[key]) {
3777
+ out ??= { ...s };
3778
+ out[key] = next;
3779
+ }
3780
+ }
3781
+ return out ?? s;
3782
+ }
3783
+ return s;
3784
+ };
3785
+ return {
3786
+ current: walk(stored, today, v11, []),
3787
+ confirmed,
3788
+ differing
3789
+ };
3790
+ }
3791
+ function containsMask(value) {
3792
+ if (value === "***") return true;
3793
+ if (Array.isArray(value)) return value.some(containsMask);
3794
+ if (isPlainObject(value)) return Object.values(value).some(containsMask);
3795
+ return false;
3796
+ }
3797
+ function jsonEqual(a, b) {
3798
+ return canonicalJson(a) === canonicalJson(b);
3799
+ }
3800
+ function canonicalJson(value) {
3801
+ return JSON.stringify(value, (_key, node) => {
3802
+ if (node === null || typeof node !== "object" || Array.isArray(node)) return node;
3803
+ const sorted = Object.create(null);
3804
+ for (const key of Object.keys(node).sort()) sorted[key] = node[key];
3805
+ return sorted;
3806
+ });
3807
+ }
3808
+ const PREVIOUS_NOECHO_VALUE = "(previous NoEcho value)";
3809
+ function noEchoParametersReadBy(node, sources) {
3810
+ return [...sources.parameters].filter((name) => readsNoEchoSource(node, {
3811
+ parameters: /* @__PURE__ */ new Set([name]),
3812
+ ...sources.conditions !== void 0 && { conditions: sources.conditions }
3813
+ }));
3814
+ }
3815
+ function noEchoComparison(options) {
3816
+ const { sources, values, minNeedleLength, publicTokens } = options;
3817
+ return ({ templateProperties, desired, current }) => {
3818
+ const coordinates = noEchoCoordinatesOf(templateProperties, desired, sources);
3819
+ const needles = [];
3820
+ for (const name of noEchoParametersReadBy(templateProperties, sources)) {
3821
+ const value = values[name];
3822
+ const leaves = Array.isArray(value) ? value : [value];
3823
+ for (const leaf of leaves) if (typeof leaf === "string" && leaf.length >= minNeedleLength && !isSingleDynamicReferenceToken(leaf)) needles.push(leaf);
3824
+ }
3825
+ if (coordinates.length === 0 && needles.length === 0) return void 0;
3826
+ let masked = maskAtCoordinates(desired, coordinates);
3827
+ if (needles.length > 0) {
3828
+ const walk = (node) => {
3829
+ if (typeof node === "string") {
3830
+ if (node === "***") return node;
3831
+ const outside = node.replace(DYNAMIC_REFERENCE_TOKEN_SCAN, "");
3832
+ return needles.some((needle) => node === needle || publicTokens?.has(needle) !== true && outside.includes(needle)) ? "***" : node;
3833
+ }
3834
+ if (Array.isArray(node)) {
3835
+ const out = node.map(walk);
3836
+ return out.some((item, i) => item !== node[i]) ? out : node;
3837
+ }
3838
+ if (isPlainObject(node)) {
3839
+ let out;
3840
+ for (const [key, child] of Object.entries(node)) {
3841
+ const next = walk(child);
3842
+ if (next !== child) {
3843
+ out ??= { ...node };
3844
+ out[key] = next;
3845
+ }
3846
+ }
3847
+ return out ?? node;
3848
+ }
3849
+ return node;
3850
+ };
3851
+ masked = walk(masked);
3852
+ }
3853
+ const witnessed = witnessNormalize(current, desired, masked);
3854
+ const shown = maskAtCoordinatesWith(witnessed.current, witnessed.differing, PREVIOUS_NOECHO_VALUE);
3855
+ return {
3856
+ desired: masked,
3857
+ current: shown
3858
+ };
3859
+ };
3860
+ }
3861
+ function maskAtCoordinatesWith(bag, coordinates, replacement) {
3862
+ let root = bag;
3863
+ for (const coordinate of coordinates) {
3864
+ if (coordinate.length === 0 || !readAt(root, coordinate).found) continue;
3865
+ root = cloneContainer(root);
3866
+ let node = root;
3867
+ for (let i = 0; i < coordinate.length - 1; i++) {
3868
+ const segment = coordinate[i];
3869
+ const next = cloneContainer(node[segment]);
3870
+ node[segment] = next;
3871
+ node = next;
3872
+ }
3873
+ node[coordinate[coordinate.length - 1]] = replacement;
3874
+ }
3875
+ return root;
3876
+ }
3877
+ function noEchoOutputsComparison(templateValues, sources) {
3878
+ return (current, desired) => {
3879
+ const coordinates = noEchoCoordinatesOf(templateValues, desired, sources);
3880
+ const maskedKeys = new Set(coordinates.map((coordinate) => coordinate[0]).filter((key) => typeof key === "string"));
3881
+ const maskedValues = new Set([...maskedKeys].map((key) => canonicalJson(desired[key])));
3882
+ for (const key of Object.keys(desired)) if (!Object.hasOwn(templateValues, key) && maskedValues.has(canonicalJson(desired[key]))) maskedKeys.add(key);
3883
+ if (maskedKeys.size === 0) return {
3884
+ current,
3885
+ desired,
3886
+ masked: []
3887
+ };
3888
+ const maskedDesired = { ...desired };
3889
+ for (const key of maskedKeys) maskedDesired[key] = maskWholeValue(desired[key]);
3890
+ let normalized = current;
3891
+ if (current !== void 0 && current !== null && typeof current === "object") for (const key of maskedKeys) {
3892
+ if (!Object.hasOwn(current, key)) continue;
3893
+ const stored = current[key];
3894
+ if (containsMask(stored)) continue;
3895
+ normalized = { ...normalized ?? {} };
3896
+ normalized[key] = canonicalJson(stored) === canonicalJson(desired[key]) ? maskedDesired[key] : PREVIOUS_NOECHO_VALUE;
3897
+ }
3898
+ return {
3899
+ current: normalized,
3900
+ desired: maskedDesired,
3901
+ masked: [...maskedKeys]
3902
+ };
3903
+ };
3904
+ }
3905
+ function maskedLeafCoordinatesOf(bag) {
3906
+ const coordinates = [];
3907
+ const ancestors = /* @__PURE__ */ new Set();
3908
+ const walk = (node, path) => {
3909
+ if (node === "***") {
3910
+ coordinates.push(path);
3911
+ return;
3912
+ }
3913
+ if (node === null || typeof node !== "object" || ancestors.has(node)) return;
3914
+ ancestors.add(node);
3915
+ if (Array.isArray(node)) node.forEach((item, index) => walk(item, [...path, index]));
3916
+ else for (const [key, child] of Object.entries(node)) walk(child, [...path, key]);
3917
+ ancestors.delete(node);
3918
+ };
3919
+ walk(bag, []);
3920
+ return coordinates;
3921
+ }
3922
+ const passedNoEchoParameters = /* @__PURE__ */ new WeakMap();
3923
+ function recordPassedNoEchoParameters(bag, parameters, sources) {
3924
+ const names = /* @__PURE__ */ new Set();
3925
+ if (sources !== void 0 && parameters !== null && typeof parameters === "object") {
3926
+ for (const [name, value] of Object.entries(parameters)) if (readsNoEchoSource(value, sources)) names.add(name);
3927
+ }
3928
+ if (names.size > 0) passedNoEchoParameters.set(bag, names);
3929
+ else passedNoEchoParameters.delete(bag);
3930
+ }
3931
+ function passedNoEchoParametersOf(bag) {
3932
+ return bag === void 0 ? void 0 : passedNoEchoParameters.get(bag);
3933
+ }
3934
+
3935
+ //#endregion
3936
+ export { recordLogOnlyParameterValue as $, crossStackSourceKey as $t, recordIntrinsicLeafResolutionAs as A, inheritedParameterExpression as At, passProvenanceFor as B, standsForInheritedSecret as Bt, carryRedactionMarks as C, WHOLE_DYNAMIC_REFERENCE_PATTERN as Cn, DYNAMIC_REFERENCE_TOKEN_SCAN as Ct, markSameGenerationBag as D, pathCrossesDottedKey as Dt, markLeafCertifiedBag as E, maskMatchSpans as En, isUncertifiedBaselineMaskPosition as Et, redactForState as F, recordInheritedPseudoParameters as Ft, carryLogOnlyValues as G, freshNoEchoLeafPositions as Gt, recordReferenceProvenanceEntry as H, withdrawRenderedParameterSpelling as Ht, foldRecording as I, recordNestedStackParameterExpressions as It, hasLogOnlyValues as J, physicalNameCopiesOf as Jt, carryLogOnlyValuesCarriedBy as K, recordRecoverableMaskedOutput as Kt, openRecordingScope as L, redactInheritedParameterValue as Lt, resolvedPlaintextOf as M, inheritedRenderedToken as Mt, mixedLiteralAnswered as N, recordInheritedParameterRead as Nt, mergeResolvedPairs as O, identityKeyFor as Ot, withLegacyMixedScan as P, recordInheritedParameterValue as Pt, printingCorpusOf as Q, recordTakenIfBranch as Qt, recordSecretResolution as R, rereadParameterExpression as Rt, liveMatchesUnresolvedTokenFrame as S, withoutNoEchoParameterEntries as Sn, spanNamesResolvableService as St, isSameGenerationBag as T, isSingleDynamicReferenceToken as Tn, dynamicReferenceTokens as Tt, recordReferenceResolution as U, carriesFreshNoEchoValue as Ut, passReadVersionsFor as V, templateRereadsAccountId as Vt, snapshotPassProvenance as W, embedsFreshNoEchoValue as Wt, literalSplitDelimitersOf as X, recordPhysicalNameCopy as Xt, hasMaskableValues as Y, recordAttributeIdCopy as Yt, logOnlyValueCount as Z, recordDroppedArrayElements as Zt, recordPassedNoEchoParameters as _, recordMaskOnlyValuesIn as _n, replaceWholeLeaves as _t, maskReadbackAtCoordinates as a, freshNoEchoValuesOf as an, errorCauseChain as at, valueAtCoordinate as b, recordProvenPublicExpression as bn, withPhysicalNameCopies as bt, noEchoComparison as c, isScopedSecretVerdict as cn, maskSecretsInText as ct, noEchoOutputsComparison as d, noEchoParameterPlaintextsOf as dn, legacyLeavesWithoutMask as dt, recordCrossStackExpression as en, recordLogOnlySplitFragments as et, noEchoParameterNodesAt as f, pinScopedSecretVerdict as fn, legacyReferenceLeavesOnly as ft, readsNoEchoSource as g, recordMaskOnlyValue as gn, replaceEmbeddedPhysicalNameRewrites as gt, readbackPathFor as h, recordFreshNoEchoValuesIn as hn, repairPhysicalNameCopyLeaves as ht, maskAtCoordinates as i, contradictProvenPublicExpression as in, createSecretMasker as it, redactionClaimOf as j, inheritedPseudoValue as jt, recordIntrinsicLeafResolution as k, inheritNestedStackParameterAssociations as kt, noEchoCoordinatesOf as l, isSecretExpressionByVerdictOrSpelling as ln, differingStringLeaves as lt, passedNoEchoParametersOf as m, recordDerivedMaskOnlyValue as mn, repairEmbeddedPhysicalNamesTowards as mt, canonicalJson as n, carriesSecretMask as nn, shareLogOnlyValues as nt, maskWholeValue as o, isMaskOnlyPlaintext as on, maskRecordedSecretsInText as ot, noEchoParametersReadBy as p, provenPublicValue as pn, overlapRuleAnswered as pt, createUnionSecretMasker as q, recoverMaskedOutput as qt, isMarkedCoordinate as r, carryFreshNoEchoMark as rn, unionOfSecretBags as rt, maskedLeafCoordinatesOf as s, isNoEchoParameterPlaintext as sn, maskSecretsInError as st, canonicalCoordinates as t, splitGetAttStringForm as tn, recordLogOnlyValue as tt, noEchoLeavesOf as u, markNoEchoParameterClass as un, embeddedPhysicalNameRewrites as ut, recordedNoEchoValuesOf as v, recordNoEchoAttributeValues as vn, scrubResourceRecord as vt, intrinsicLeafResolutionOf as w, isDynamicReferenceString as wn, arrayPairsWhateverDrops as wt, witnessNormalize as x, wholeStringLeavesOf as xn, withoutPhysicalNameCopies as xt, replaceAtCoordinates as y, recordNoEchoParameterFreshValue as yn, withLegacyOverlapScan as yt, hasPassProvenance as z, splitOutsideDynamicReferences as zt };
3937
+ //# sourceMappingURL=secret-redaction-BHWS-PZK.js.map