@go-to-k/cdkd 0.285.12 → 0.285.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{asg-provider-CXkE2U3z.js → asg-provider-BvIH9Ivw.js} +11 -4
- package/dist/asg-provider-BvIH9Ivw.js.map +1 -0
- package/dist/cli.js +2 -2
- package/dist/{deploy-engine-C3epVkTj.js → deploy-engine-CA50haPO.js} +367 -76
- package/dist/deploy-engine-CA50haPO.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/{program-C5evYwxk.js → program-BSanOkkV.js} +370 -149
- package/dist/{program-C5evYwxk.js.map → program-BSanOkkV.js.map} +1 -1
- package/dist/{version-CS-eKBJA.js → version-DP5kGlzj.js} +2 -2
- package/dist/{version-CS-eKBJA.js.map → version-DP5kGlzj.js.map} +1 -1
- package/package.json +1 -7
- package/dist/asg-provider-CXkE2U3z.js.map +0 -1
- package/dist/deploy-engine-C3epVkTj.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { _ as withStackName, d as applyDefaultNameForFallback, f as generateResourceName, h as looksLikeCdkdGeneratedName, m as getCurrentStackName, n as getLogger, p as generateResourceNameWithFallback, r as isStdoutReservedForPayload, s as getLiveRenderer } from "./logger-Dw-3y48G.js";
|
|
2
2
|
import { t as awsClientDefaults } from "./aws-client-defaults-D-iYiIJ6.js";
|
|
3
|
-
import { t as getCdkdVersion } from "./version-
|
|
3
|
+
import { t as getCdkdVersion } from "./version-DP5kGlzj.js";
|
|
4
4
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5
5
|
import { randomUUID } from "node:crypto";
|
|
6
6
|
import { CreateBucketCommand, DeleteObjectCommand, DeleteObjectsCommand, GetBucketLocationCommand, GetObjectCommand, HeadBucketCommand, HeadObjectCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchKey, PutBucketEncryptionCommand, PutBucketPolicyCommand, PutObjectCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
|
|
@@ -2705,7 +2705,10 @@ var AZContextProvider = class {
|
|
|
2705
2705
|
async resolve(props) {
|
|
2706
2706
|
const region = props["region"] || this.awsConfig?.region;
|
|
2707
2707
|
this.logger.debug(`Fetching availability zones for region: ${region}`);
|
|
2708
|
-
const client = new EC2Client({
|
|
2708
|
+
const client = new EC2Client({
|
|
2709
|
+
...awsClientDefaults(),
|
|
2710
|
+
...region && { region }
|
|
2711
|
+
});
|
|
2709
2712
|
try {
|
|
2710
2713
|
const azs = ((await client.send(new DescribeAvailabilityZonesCommand({}))).AvailabilityZones ?? []).filter((az) => az.State === "available").map((az) => az.ZoneName).filter(Boolean).sort();
|
|
2711
2714
|
this.logger.debug(`Found ${azs.length} availability zones: ${azs.join(", ")}`);
|
|
@@ -2735,7 +2738,10 @@ var SSMContextProvider = class {
|
|
|
2735
2738
|
const parameterName = props["parameterName"];
|
|
2736
2739
|
if (!parameterName) throw new Error("SSM context provider requires parameterName property");
|
|
2737
2740
|
this.logger.debug(`Reading SSM parameter: ${parameterName} (region: ${region})`);
|
|
2738
|
-
const client = new SSMClient({
|
|
2741
|
+
const client = new SSMClient({
|
|
2742
|
+
...awsClientDefaults(),
|
|
2743
|
+
...region && { region }
|
|
2744
|
+
});
|
|
2739
2745
|
try {
|
|
2740
2746
|
const response = await client.send(new GetParameterCommand({ Name: parameterName }));
|
|
2741
2747
|
if (!response.Parameter || response.Parameter.Value === void 0) {
|
|
@@ -2774,7 +2780,10 @@ var HostedZoneContextProvider = class {
|
|
|
2774
2780
|
const vpcId = props["vpcId"];
|
|
2775
2781
|
if (!domainName) throw new Error("Hosted zone context provider requires domainName property");
|
|
2776
2782
|
this.logger.debug(`Looking up hosted zone: ${domainName} (private: ${privateZone})`);
|
|
2777
|
-
const client = new Route53Client({
|
|
2783
|
+
const client = new Route53Client({
|
|
2784
|
+
...awsClientDefaults(),
|
|
2785
|
+
...region && { region }
|
|
2786
|
+
});
|
|
2778
2787
|
try {
|
|
2779
2788
|
const zones = (await client.send(new ListHostedZonesByNameCommand({
|
|
2780
2789
|
DNSName: domainName,
|
|
@@ -2825,7 +2834,10 @@ var VpcContextProvider = class {
|
|
|
2825
2834
|
const subnetGroupNameTag = props["subnetGroupNameTag"] || "aws-cdk:subnet-name";
|
|
2826
2835
|
const returnVpnGateways = props["returnVpnGateways"];
|
|
2827
2836
|
this.logger.debug(`Looking up VPC (region: ${region}, filter: ${JSON.stringify(filter)})`);
|
|
2828
|
-
const client = new EC2Client({
|
|
2837
|
+
const client = new EC2Client({
|
|
2838
|
+
...awsClientDefaults(),
|
|
2839
|
+
...region && { region }
|
|
2840
|
+
});
|
|
2829
2841
|
try {
|
|
2830
2842
|
const vpcFilters = filter ? Object.entries(filter).map(([name, value]) => ({
|
|
2831
2843
|
Name: name,
|
|
@@ -2988,7 +3000,10 @@ var CcApiContextProvider = class {
|
|
|
2988
3000
|
const ignoreErrorOnMissingContext = props["ignoreErrorOnMissingContext"];
|
|
2989
3001
|
if (!typeName) throw new Error("CC API context provider requires typeName property");
|
|
2990
3002
|
this.logger.debug(`CC API lookup: ${typeName}${exactIdentifier ? ` (id: ${exactIdentifier})` : ""} (region: ${region})`);
|
|
2991
|
-
const client = new CloudControlClient({
|
|
3003
|
+
const client = new CloudControlClient({
|
|
3004
|
+
...awsClientDefaults(),
|
|
3005
|
+
...region && { region }
|
|
3006
|
+
});
|
|
2992
3007
|
try {
|
|
2993
3008
|
let resources;
|
|
2994
3009
|
if (exactIdentifier) {
|
|
@@ -3115,7 +3130,10 @@ var AmiContextProvider = class {
|
|
|
3115
3130
|
const owners = props["owners"];
|
|
3116
3131
|
const filters = props["filters"];
|
|
3117
3132
|
this.logger.debug(`Looking up AMI (region: ${region})`);
|
|
3118
|
-
const client = new EC2Client({
|
|
3133
|
+
const client = new EC2Client({
|
|
3134
|
+
...awsClientDefaults(),
|
|
3135
|
+
...region && { region }
|
|
3136
|
+
});
|
|
3119
3137
|
try {
|
|
3120
3138
|
const ec2Filters = filters ? Object.entries(filters).map(([name, values]) => ({
|
|
3121
3139
|
Name: name,
|
|
@@ -3155,7 +3173,10 @@ var SecurityGroupContextProvider = class {
|
|
|
3155
3173
|
const securityGroupName = props["securityGroupName"];
|
|
3156
3174
|
const vpcId = props["vpcId"];
|
|
3157
3175
|
this.logger.debug(`Looking up security group (id: ${securityGroupId}, name: ${securityGroupName}, region: ${region})`);
|
|
3158
|
-
const client = new EC2Client({
|
|
3176
|
+
const client = new EC2Client({
|
|
3177
|
+
...awsClientDefaults(),
|
|
3178
|
+
...region && { region }
|
|
3179
|
+
});
|
|
3159
3180
|
try {
|
|
3160
3181
|
const filters = [];
|
|
3161
3182
|
if (securityGroupId) filters.push({
|
|
@@ -3206,7 +3227,10 @@ var LoadBalancerContextProvider = class {
|
|
|
3206
3227
|
const loadBalancerArn = props["loadBalancerArn"];
|
|
3207
3228
|
const loadBalancerType = props["loadBalancerType"];
|
|
3208
3229
|
this.logger.debug(`Looking up load balancer (arn: ${loadBalancerArn}, region: ${region})`);
|
|
3209
|
-
const client = new ElasticLoadBalancingV2Client({
|
|
3230
|
+
const client = new ElasticLoadBalancingV2Client({
|
|
3231
|
+
...awsClientDefaults(),
|
|
3232
|
+
...region && { region }
|
|
3233
|
+
});
|
|
3210
3234
|
try {
|
|
3211
3235
|
let lbs = (await client.send(new DescribeLoadBalancersCommand({ ...loadBalancerArn && { LoadBalancerArns: [loadBalancerArn] } }))).LoadBalancers ?? [];
|
|
3212
3236
|
if (loadBalancerType) lbs = lbs.filter((lb) => lb.Type === loadBalancerType);
|
|
@@ -3245,7 +3269,10 @@ var LoadBalancerListenerContextProvider = class {
|
|
|
3245
3269
|
const listenerPort = props["listenerPort"];
|
|
3246
3270
|
const listenerProtocol = props["listenerProtocol"];
|
|
3247
3271
|
this.logger.debug(`Looking up load balancer listener (arn: ${listenerArn}, lb: ${loadBalancerArn}, region: ${region})`);
|
|
3248
|
-
const client = new ElasticLoadBalancingV2Client({
|
|
3272
|
+
const client = new ElasticLoadBalancingV2Client({
|
|
3273
|
+
...awsClientDefaults(),
|
|
3274
|
+
...region && { region }
|
|
3275
|
+
});
|
|
3249
3276
|
try {
|
|
3250
3277
|
let listeners = (await client.send(new DescribeListenersCommand({
|
|
3251
3278
|
...listenerArn && { ListenerArns: [listenerArn] },
|
|
@@ -3286,7 +3313,10 @@ var KeyContextProvider = class {
|
|
|
3286
3313
|
const aliasName = props["aliasName"];
|
|
3287
3314
|
if (!aliasName) throw new Error("Key context provider requires aliasName property");
|
|
3288
3315
|
this.logger.debug(`Looking up KMS key by alias: ${aliasName} (region: ${region})`);
|
|
3289
|
-
const client = new KMSClient({
|
|
3316
|
+
const client = new KMSClient({
|
|
3317
|
+
...awsClientDefaults(),
|
|
3318
|
+
...region && { region }
|
|
3319
|
+
});
|
|
3290
3320
|
try {
|
|
3291
3321
|
const normalizedAlias = aliasName.startsWith("alias/") ? aliasName : `alias/${aliasName}`;
|
|
3292
3322
|
let nextMarker;
|
|
@@ -4138,6 +4168,7 @@ async function uploadCfnTemplate(args) {
|
|
|
4138
4168
|
...s3ClientOpts?.credentials && { credentials: s3ClientOpts.credentials }
|
|
4139
4169
|
});
|
|
4140
4170
|
const s3 = new S3Client({
|
|
4171
|
+
...awsClientDefaults({ profile: s3ClientOpts?.profile }),
|
|
4141
4172
|
region,
|
|
4142
4173
|
...s3ClientOpts?.profile && { profile: s3ClientOpts.profile },
|
|
4143
4174
|
...s3ClientOpts?.credentials && { credentials: s3ClientOpts.credentials }
|
|
@@ -4535,7 +4566,10 @@ async function expandMacrosAttempt(template, opts, logger) {
|
|
|
4535
4566
|
const serialized = JSON.stringify(template);
|
|
4536
4567
|
const parameters = buildParameterValues(template, logger);
|
|
4537
4568
|
ownsClient = opts.cfnClient === void 0;
|
|
4538
|
-
cfn = opts.cfnClient ?? new CloudFormationClient({
|
|
4569
|
+
cfn = opts.cfnClient ?? new CloudFormationClient({
|
|
4570
|
+
...awsClientDefaults(),
|
|
4571
|
+
region
|
|
4572
|
+
});
|
|
4539
4573
|
let templateInput;
|
|
4540
4574
|
if (serialized.length > 1048576) throw new MacroExpansionError(`Template is ${serialized.length} bytes, which exceeds CloudFormation's ${CFN_TEMPLATE_URL_LIMIT}-byte TemplateURL ceiling for macro expansion. Shrink inline payloads (move inline lambda.Code.ZipFile to lambda.Code.fromAsset, etc.) or split the stack before retrying.`);
|
|
4541
4575
|
if (serialized.length <= 51200) templateInput = { TemplateBody: serialized };
|
|
@@ -5143,7 +5177,10 @@ var Synthesizer = class {
|
|
|
5143
5177
|
const region = explicitRegion || await resolveSdkDefaultRegion(options.profile);
|
|
5144
5178
|
let accountId;
|
|
5145
5179
|
try {
|
|
5146
|
-
const stsClient = new STSClient({
|
|
5180
|
+
const stsClient = new STSClient({
|
|
5181
|
+
...awsClientDefaults(),
|
|
5182
|
+
...region && { region }
|
|
5183
|
+
});
|
|
5147
5184
|
accountId = (await stsClient.send(new GetCallerIdentityCommand({}))).Account;
|
|
5148
5185
|
stsClient.destroy();
|
|
5149
5186
|
} catch {
|
|
@@ -5235,7 +5272,10 @@ var Synthesizer = class {
|
|
|
5235
5272
|
if (!region) throw new SynthesisError(`Stack(s) [${stacksWithMacros.map((s) => s.stackName).join(", ")}] use CloudFormation macros (Transform / Fn::Transform) but cdkd could not resolve an AWS region for the expansion round-trip. Set AWS_REGION, pass --region <r>, or set env: { region: '<r>' } in your CDK Stack constructor.`);
|
|
5236
5273
|
let accountId = resolved?.accountId;
|
|
5237
5274
|
if (resolved === void 0 && !options.stateBucket) try {
|
|
5238
|
-
const stsClient = new STSClient({
|
|
5275
|
+
const stsClient = new STSClient({
|
|
5276
|
+
...awsClientDefaults(),
|
|
5277
|
+
region
|
|
5278
|
+
});
|
|
5239
5279
|
accountId = (await stsClient.send(new GetCallerIdentityCommand({}))).Account;
|
|
5240
5280
|
stsClient.destroy();
|
|
5241
5281
|
} catch {
|
|
@@ -5281,7 +5321,10 @@ var Synthesizer = class {
|
|
|
5281
5321
|
async function resolveSdkDefaultRegion(profile) {
|
|
5282
5322
|
let client;
|
|
5283
5323
|
try {
|
|
5284
|
-
client = new STSClient({
|
|
5324
|
+
client = new STSClient({
|
|
5325
|
+
...awsClientDefaults({ profile }),
|
|
5326
|
+
...profile && { profile }
|
|
5327
|
+
});
|
|
5285
5328
|
return await client.config.region() || void 0;
|
|
5286
5329
|
} catch {
|
|
5287
5330
|
return;
|
|
@@ -5471,7 +5514,10 @@ var FileAssetPublisher = class {
|
|
|
5471
5514
|
const objectKey = this.resolvePlaceholders(dest.objectKey, accountId, region);
|
|
5472
5515
|
const destRegion = dest.region ? this.resolvePlaceholders(dest.region, accountId, region) : region;
|
|
5473
5516
|
this.logger.debug(`Publishing file asset ${asset.displayName || assetHash} → s3://${bucketName}/${objectKey}`);
|
|
5474
|
-
const client = new S3Client({
|
|
5517
|
+
const client = new S3Client({
|
|
5518
|
+
...awsClientDefaults(),
|
|
5519
|
+
region: destRegion
|
|
5520
|
+
});
|
|
5475
5521
|
try {
|
|
5476
5522
|
if (await this.objectExists(client, bucketName, objectKey)) {
|
|
5477
5523
|
this.logger.debug(`Asset already exists, skipping: s3://${bucketName}/${objectKey}`);
|
|
@@ -6095,7 +6141,10 @@ var DockerAssetPublisher = class {
|
|
|
6095
6141
|
const destRegion = dest.region ? this.resolvePlaceholders(dest.region, accountId, region) : region;
|
|
6096
6142
|
const ecrUri = `${accountId}.dkr.ecr.${destRegion}.${ecrUrlSuffix(destRegion)}/${repositoryName}:${imageTag}`;
|
|
6097
6143
|
this.logger.debug(`Publishing Docker image ${asset.displayName || assetHash} → ${ecrUri}`);
|
|
6098
|
-
const client = new ECRClient({
|
|
6144
|
+
const client = new ECRClient({
|
|
6145
|
+
...awsClientDefaults(),
|
|
6146
|
+
region: destRegion
|
|
6147
|
+
});
|
|
6099
6148
|
try {
|
|
6100
6149
|
if (await this.imageExists(client, repositoryName, imageTag)) {
|
|
6101
6150
|
this.logger.debug(`Image already exists, skipping: ${ecrUri}`);
|
|
@@ -6131,7 +6180,10 @@ var DockerAssetPublisher = class {
|
|
|
6131
6180
|
const imageTag = this.resolvePlaceholders(dest.imageTag, accountId, region);
|
|
6132
6181
|
const destRegion = dest.region ? this.resolvePlaceholders(dest.region, accountId, region) : region;
|
|
6133
6182
|
const ecrUri = `${accountId}.dkr.ecr.${destRegion}.${ecrUrlSuffix(destRegion)}/${repositoryName}:${imageTag}`;
|
|
6134
|
-
const client = new ECRClient({
|
|
6183
|
+
const client = new ECRClient({
|
|
6184
|
+
...awsClientDefaults(),
|
|
6185
|
+
region: destRegion
|
|
6186
|
+
});
|
|
6135
6187
|
try {
|
|
6136
6188
|
if (await this.imageExists(client, repositoryName, imageTag)) {
|
|
6137
6189
|
this.logger.debug(`Image already exists, skipping: ${ecrUri}`);
|
|
@@ -7427,6 +7479,7 @@ function createAssetRedirectResolver(opts) {
|
|
|
7427
7479
|
accountIdPromise ??= (async () => {
|
|
7428
7480
|
const { STSClient, GetCallerIdentityCommand } = await import("@aws-sdk/client-sts");
|
|
7429
7481
|
const stsClient = new STSClient({
|
|
7482
|
+
...awsClientDefaults({ profile: opts.profile }),
|
|
7430
7483
|
region: opts.stsRegion,
|
|
7431
7484
|
...opts.profile && { profile: opts.profile }
|
|
7432
7485
|
});
|
|
@@ -7706,7 +7759,10 @@ var AssetPublisher = class {
|
|
|
7706
7759
|
let accountId = options.accountId;
|
|
7707
7760
|
if (!accountId) {
|
|
7708
7761
|
const { STSClient, GetCallerIdentityCommand } = await import("@aws-sdk/client-sts");
|
|
7709
|
-
const stsClient = new STSClient({
|
|
7762
|
+
const stsClient = new STSClient({
|
|
7763
|
+
...awsClientDefaults(),
|
|
7764
|
+
region
|
|
7765
|
+
});
|
|
7710
7766
|
accountId = (await stsClient.send(new GetCallerIdentityCommand({}))).Account;
|
|
7711
7767
|
stsClient.destroy();
|
|
7712
7768
|
}
|
|
@@ -9552,6 +9608,99 @@ var LockManager = class {
|
|
|
9552
9608
|
/** Fixed marker substituted for a secret value in log / error output. */
|
|
9553
9609
|
const SECRET_MASK = "***";
|
|
9554
9610
|
/**
|
|
9611
|
+
* The UNCOLLAPSED companion of a {@link RecordedSecretValues} map: for each map
|
|
9612
|
+
* instance, every `expression -> plaintext` pair the resolver recorded INTO IT,
|
|
9613
|
+
* keyed by EXPRESSION (issue [#2485](https://github.com/go-to-k/cdkd/issues/2485)).
|
|
9614
|
+
*
|
|
9615
|
+
* WHY IT EXISTS. The map is keyed by PLAINTEXT, so two expressions resolving to
|
|
9616
|
+
* one value keep ONE entry — whichever the resolver recorded last. A WHOLE-token
|
|
9617
|
+
* leaf is immune (the position pass copies its own source), but a leaf that
|
|
9618
|
+
* EMBEDS a token in a literal string is redacted by the value scan, which can
|
|
9619
|
+
* only write the map's surviving expression: the versioned sibling's, for a
|
|
9620
|
+
* template that spells the un-versioned one, and the next deploy diffs that
|
|
9621
|
+
* leaf forever. Recovering the losing expression needs evidence the map has
|
|
9622
|
+
* discarded, and it has to be PASS-LOCAL: `recordedSecretExpressions` is
|
|
9623
|
+
* process-wide and says only that an expression IS secret, never what it
|
|
9624
|
+
* resolved to in THIS resource — so it cannot tell "the source token lost the
|
|
9625
|
+
* map slot to its sibling" from "the source token was never resolved here"
|
|
9626
|
+
* (a previous generation's bag, where writing today's expression over the
|
|
9627
|
+
* framed value would record something that was never deployed).
|
|
9628
|
+
*
|
|
9629
|
+
* Keyed by the map INSTANCE, so the evidence is exactly as pass-local as the
|
|
9630
|
+
* map itself: a map the resolver populated (the deploy's `perResourceSecrets`
|
|
9631
|
+
* entry, and equally the map drift / scrub / import hand their own resolution)
|
|
9632
|
+
* carries the pairs of THAT resolution, while a map the resolver did not
|
|
9633
|
+
* populate — a derived needle map, a nested-stack inheritance copy, a
|
|
9634
|
+
* `new Map(secrets)` copy — starts with no entries here and takes the
|
|
9635
|
+
* pre-#2485 fall-through, the safe direction. A copy loses the evidence
|
|
9636
|
+
* deliberately: a copy is not the pass that resolved anything.
|
|
9637
|
+
*
|
|
9638
|
+
* `CONFLICTING_PLAINTEXT` marks an expression this map saw resolve to TWO
|
|
9639
|
+
* values (a region-pinned re-resolution of one spelling, say); it then vouches
|
|
9640
|
+
* for nothing, which is the same "answer nothing you cannot prove" rule
|
|
9641
|
+
* {@link plaintextIndexOf} applies to the collapsed map's reverse index.
|
|
9642
|
+
*/
|
|
9643
|
+
const resolvedPairsOf = /* @__PURE__ */ new WeakMap();
|
|
9644
|
+
/**
|
|
9645
|
+
* Record that `expression` resolved to `plaintext` in the pass that owns
|
|
9646
|
+
* `secrets` — the resolver's recording seam calls this beside its
|
|
9647
|
+
* `secrets.set(plaintext, expression)`, so the two never disagree about which
|
|
9648
|
+
* pass the evidence belongs to. Mask-only map entries (value `SECRET_MASK`)
|
|
9649
|
+
* never pass through that seam — they came from no `{{resolve:...}}` token —
|
|
9650
|
+
* so nothing here special-cases the mask string: a secret whose plaintext
|
|
9651
|
+
* happens to BE `***` is a secret like any other.
|
|
9652
|
+
*/
|
|
9653
|
+
function recordResolvedPair(secrets, expression, plaintext) {
|
|
9654
|
+
let pairs = resolvedPairsOf.get(secrets);
|
|
9655
|
+
if (pairs === void 0) {
|
|
9656
|
+
pairs = /* @__PURE__ */ new Map();
|
|
9657
|
+
resolvedPairsOf.set(secrets, pairs);
|
|
9658
|
+
}
|
|
9659
|
+
const previous = pairs.get(expression);
|
|
9660
|
+
if (previous === void 0) pairs.set(expression, plaintext);
|
|
9661
|
+
else if (previous !== plaintext) pairs.set(expression, CONFLICTING_PLAINTEXT);
|
|
9662
|
+
}
|
|
9663
|
+
/**
|
|
9664
|
+
* Carry the resolved pairs of `from` into `to`, for the one copy of a
|
|
9665
|
+
* resolver-populated map that POSITIONS anything: the deploy engine accumulates
|
|
9666
|
+
* each stack's output resolution into its `outputSecrets` bag entry by entry,
|
|
9667
|
+
* and without this the copy would keep the collapsed entries while dropping the
|
|
9668
|
+
* evidence — so a literal `Output` embedding one of two same-plaintext
|
|
9669
|
+
* references would fall back to the value scan and persist the sibling's
|
|
9670
|
+
* expression. The engine's other entry-by-entry copy — an `Export.Name`'s
|
|
9671
|
+
* secrets into the pass map — deliberately does NOT call this: a name never
|
|
9672
|
+
* positions a leaf, a value re-using the same token records its own pair at
|
|
9673
|
+
* the seam, and the only thing the merge could add is a CONFLICT (a
|
|
9674
|
+
* non-cacheable `{{resolve:ssm:X}}` whose value moved between the value pass
|
|
9675
|
+
* and the name's resolution), which would destroy positioning the value pass
|
|
9676
|
+
* had earned. A pair that conflicts across the two maps is marked conflicting
|
|
9677
|
+
* in `to`, the same rule {@link recordResolvedPair} applies within one map.
|
|
9678
|
+
*
|
|
9679
|
+
* Deliberately NOT a general "copy the map" helper: every other new map is a
|
|
9680
|
+
* different PASS, and starting it without evidence is the safe direction.
|
|
9681
|
+
*/
|
|
9682
|
+
function mergeResolvedPairs(from, to) {
|
|
9683
|
+
const pairs = resolvedPairsOf.get(from);
|
|
9684
|
+
if (pairs === void 0) return;
|
|
9685
|
+
for (const [expression, plaintext] of pairs) if (typeof plaintext === "string") recordResolvedPair(to, expression, plaintext);
|
|
9686
|
+
else {
|
|
9687
|
+
let target = resolvedPairsOf.get(to);
|
|
9688
|
+
if (target === void 0) {
|
|
9689
|
+
target = /* @__PURE__ */ new Map();
|
|
9690
|
+
resolvedPairsOf.set(to, target);
|
|
9691
|
+
}
|
|
9692
|
+
target.set(expression, CONFLICTING_PLAINTEXT);
|
|
9693
|
+
}
|
|
9694
|
+
}
|
|
9695
|
+
/**
|
|
9696
|
+
* The plaintext `expression` resolved to in the pass that owns `secrets`, or
|
|
9697
|
+
* `undefined` when that pass recorded nothing for it (or two different values).
|
|
9698
|
+
*/
|
|
9699
|
+
function resolvedPlaintextOf(secrets, expression) {
|
|
9700
|
+
const recorded = resolvedPairsOf.get(secrets)?.get(expression);
|
|
9701
|
+
return typeof recorded === "string" ? recorded : void 0;
|
|
9702
|
+
}
|
|
9703
|
+
/**
|
|
9555
9704
|
* Every `{{resolve:...}}` expression this process has PROVEN resolves to a
|
|
9556
9705
|
* secret, as a SET — uncollapsed by resolved value (issue #1910).
|
|
9557
9706
|
*
|
|
@@ -10562,8 +10711,9 @@ function isKnownSecretExpression(expression, secretExpressions) {
|
|
|
10562
10711
|
return isSecretExpressionByVerdictOrSpelling(expression) || secretExpressions.has(expression);
|
|
10563
10712
|
}
|
|
10564
10713
|
/**
|
|
10565
|
-
* The
|
|
10566
|
-
* `secretsmanager` by SPELLING, and anything this process
|
|
10714
|
+
* The arms of {@link isKnownSecretExpression} that need NO pass-local set:
|
|
10715
|
+
* `secretsmanager` / `ssm-secure` by SPELLING, and anything this process
|
|
10716
|
+
* PROVED secret.
|
|
10567
10717
|
*
|
|
10568
10718
|
* Split out so the resolver can ask the same question at the issue #2059
|
|
10569
10719
|
* recording seam, where no `secretExpressions` set is in hand. It must not
|
|
@@ -10588,7 +10738,7 @@ function isKnownSecretExpression(expression, secretExpressions) {
|
|
|
10588
10738
|
* change to a store this function only reads.
|
|
10589
10739
|
*/
|
|
10590
10740
|
function isSecretExpressionByVerdictOrSpelling(expression) {
|
|
10591
|
-
return expression.startsWith("{{resolve:secretsmanager:") || isRecordedSecretExpression(expression);
|
|
10741
|
+
return expression.startsWith("{{resolve:secretsmanager:") || expression.startsWith("{{resolve:ssm-secure:") || isRecordedSecretExpression(expression);
|
|
10592
10742
|
}
|
|
10593
10743
|
/**
|
|
10594
10744
|
* The character class a `{{resolve:...}}` reference's INNER text is built from,
|
|
@@ -11132,6 +11282,124 @@ function positionByIntrinsicSkeleton(bag, source, secrets, secretExpressions) {
|
|
|
11132
11282
|
return matched;
|
|
11133
11283
|
}
|
|
11134
11284
|
/**
|
|
11285
|
+
* The ONE-span frame shared by {@link positionByEmbeddedSpan} and
|
|
11286
|
+
* {@link learnMixedLeafNeedle}: a source holding exactly one `{{resolve:...}}`
|
|
11287
|
+
* token, and a bag that starts with the source's prefix and ends with its
|
|
11288
|
+
* suffix with something non-empty between them that is NOT itself a complete
|
|
11289
|
+
* token (an already-redacted record is a persisted answer, not a plaintext).
|
|
11290
|
+
* `undefined` for any other shape. One helper rather than two copies so the
|
|
11291
|
+
* two refusals cannot drift apart.
|
|
11292
|
+
*/
|
|
11293
|
+
function singleSpanFrame(bag, source) {
|
|
11294
|
+
const spans = dynamicReferenceSpans(source);
|
|
11295
|
+
if (spans.length !== 1) return void 0;
|
|
11296
|
+
const [span] = spans;
|
|
11297
|
+
const token = source.slice(span.start, span.end);
|
|
11298
|
+
const prefix = source.slice(0, span.start);
|
|
11299
|
+
const suffix = source.slice(span.end);
|
|
11300
|
+
if (bag.length <= prefix.length + suffix.length) return void 0;
|
|
11301
|
+
if (!bag.startsWith(prefix) || !bag.endsWith(suffix)) return void 0;
|
|
11302
|
+
const middle = bag.slice(prefix.length, bag.length - suffix.length);
|
|
11303
|
+
if (isSingleDynamicReferenceToken(middle)) return void 0;
|
|
11304
|
+
return {
|
|
11305
|
+
token,
|
|
11306
|
+
prefix,
|
|
11307
|
+
suffix,
|
|
11308
|
+
middle
|
|
11309
|
+
};
|
|
11310
|
+
}
|
|
11311
|
+
/**
|
|
11312
|
+
* Position a literal source leaf that EMBEDS exactly one `{{resolve:...}}`
|
|
11313
|
+
* token — `postgres://app-svc:{{resolve:ssm-secure:NAME}}@db/app` — by the
|
|
11314
|
+
* span the source states, writing `prefix + token + suffix` (issue
|
|
11315
|
+
* [#2485](https://github.com/go-to-k/cdkd/issues/2485)).
|
|
11316
|
+
*
|
|
11317
|
+
* WHY THE VALUE SCAN IS NOT ENOUGH HERE. The scan writes the map's surviving
|
|
11318
|
+
* expression for a plaintext, and the map keeps one expression per plaintext:
|
|
11319
|
+
* a whole-value `NAME:1` sibling that resolved LAST leaves `NAME:1` as the only
|
|
11320
|
+
* expression for the value, so the embedded leaf persists the versioned
|
|
11321
|
+
* spelling for a template that spells `NAME`, and the deploy diff — expression
|
|
11322
|
+
* against expression — reports that leaf on every run. The whole-token arm of
|
|
11323
|
+
* {@link redactByPath} is immune because it copies its own source; this arm
|
|
11324
|
+
* gives the one-span literal leaf the same immunity.
|
|
11325
|
+
*
|
|
11326
|
+
* THE EVIDENCE, and why the shape of the frame is not enough on its own: the
|
|
11327
|
+
* frame check (`bag` starts with the source's prefix and ends with its suffix,
|
|
11328
|
+
* with something between) is what {@link learnMixedLeafNeedle} already uses to
|
|
11329
|
+
* LEARN a needle, and it proves only that the bag has the source's shape. The
|
|
11330
|
+
* bag can also be a PREVIOUS generation's (`cdkd scrub`, a state-sourced walk)
|
|
11331
|
+
* with an earlier plaintext framed exactly like this, and writing today's
|
|
11332
|
+
* token over it would record an expression that was never deployed at that
|
|
11333
|
+
* position — the hazard `sourceIsSameGeneration` exists for on the whole-token
|
|
11334
|
+
* arm. So the middle must EQUAL what THIS pass recorded the source token
|
|
11335
|
+
* resolving to ({@link recordResolvedPair}, per map instance): that is evidence
|
|
11336
|
+
* of this resolution, not of shape, and it is absent by construction for every
|
|
11337
|
+
* bag this pass did not produce. It is also what keeps a PUBLIC `ssm` token
|
|
11338
|
+
* resolved (issue #1901) — the resolver records only secret verdicts — and what
|
|
11339
|
+
* keeps a mask-only `NoEcho` value out (never recorded).
|
|
11340
|
+
*
|
|
11341
|
+
* WHAT THIS EVIDENCE DOES NOT CLAIM, stated because a reviewer asked: it does
|
|
11342
|
+
* not prove the bag was produced FROM this source. A previous generation's bag
|
|
11343
|
+
* whose framed middle happens to EQUAL a plaintext this pass resolved the
|
|
11344
|
+
* source token to (`cdkd scrub` walking an old record against today's template,
|
|
11345
|
+
* or a failed deploy persisting an old bag) takes this arm and persists TODAY's
|
|
11346
|
+
* expression at that position. That is not a new claim: the value scan the
|
|
11347
|
+
* arm replaces rewrites that same plaintext onto one of THIS pass's expressions
|
|
11348
|
+
* regardless of generation — the map holds no other — so the class of answer
|
|
11349
|
+
* is unchanged and only the choice within it improves (the source's own
|
|
11350
|
+
* token rather than the map's survivor). The generation hazard this arm must
|
|
11351
|
+
* not create is the whole-token arm's: a middle that is ALREADY an expression
|
|
11352
|
+
* (a persisted answer from another generation), which the token refusal below
|
|
11353
|
+
* keeps out — and, by the same argument, any leaf the value scan would NOT
|
|
11354
|
+
* rewrite to exactly `prefix + survivor + suffix`: a middle shorter than the
|
|
11355
|
+
* scan's needle floor (an embedded 1-3 character secret stays the scan's
|
|
11356
|
+
* documented residual — issue #2516 tracks closing it with a bound that
|
|
11357
|
+
* proves the bag's generation, which this evidence does not), a whole leaf
|
|
11358
|
+
* that is itself another recorded plaintext, a needle starting in the prefix
|
|
11359
|
+
* and overlapping the middle. The
|
|
11360
|
+
* arm checks that equivalence against the scan's own answer rather than
|
|
11361
|
+
* re-deriving the scan's rules. Pinned by the cross-generation cases in
|
|
11362
|
+
* `secret-redaction-embedded-span.test.ts`.
|
|
11363
|
+
*
|
|
11364
|
+
* One shape reaches this arm that a reader may not expect: a WHOLE-token
|
|
11365
|
+
* source that FAILED the whole-token arm's `isKnownSecretExpression` gate (an
|
|
11366
|
+
* `ssm` token whose type came back unclassifiable and which lost the map slot
|
|
11367
|
+
* to a sibling). Its "frame" is empty, and if this pass recorded it resolving
|
|
11368
|
+
* to the bag it is written back as itself — an expression, and the leaf's own,
|
|
11369
|
+
* where the scan wrote the survivor. Stated so it is not mistaken for a leak.
|
|
11370
|
+
*
|
|
11371
|
+
* Everything else keeps the pre-#2485 fall-through: two or more spans (which
|
|
11372
|
+
* span produced which value is genuinely ambiguous when they share one), an
|
|
11373
|
+
* `Fn::Sub` / `Fn::Join` source (an object, not this arm at all — issue #2320's
|
|
11374
|
+
* placeholder primitive), a frame mismatch, a middle that is itself a complete
|
|
11375
|
+
* token (an already-redacted record, per the same refusal
|
|
11376
|
+
* {@link learnMixedLeafNeedle} makes), and a middle this pass cannot vouch for.
|
|
11377
|
+
*
|
|
11378
|
+
* The frame is copied from the SOURCE, not scanned. A needle occurring in the
|
|
11379
|
+
* literal frame would be a reference the template never had at that offset —
|
|
11380
|
+
* the fabricated-baseline direction {@link preferPositionDecisions} refuses —
|
|
11381
|
+
* and the whole-token arm returns its source unscanned for the same reason.
|
|
11382
|
+
*
|
|
11383
|
+
* RETURNS THE VALUE SCAN'S ANSWER ON EVERY REFUSAL, not `undefined`: the scan
|
|
11384
|
+
* is computed once, here, for `(bag, secrets)` — the arm's bound below compares
|
|
11385
|
+
* against it, and every fall-through IS it — so the compared value provably
|
|
11386
|
+
* comes from the same bag and map the arm positions. An earlier revision took
|
|
11387
|
+
* the scan as a parameter, which left the bound one wrong caller away from
|
|
11388
|
+
* comparing against a scan of some other bag with no type error.
|
|
11389
|
+
*/
|
|
11390
|
+
function positionByEmbeddedSpan(bag, source, secrets) {
|
|
11391
|
+
const scanned = redactSecretsForState(bag, secrets);
|
|
11392
|
+
const frame = singleSpanFrame(bag, source);
|
|
11393
|
+
if (frame === void 0) return scanned;
|
|
11394
|
+
const { token, prefix, suffix, middle } = frame;
|
|
11395
|
+
const recorded = resolvedPlaintextOf(secrets, token);
|
|
11396
|
+
if (recorded === void 0 || recorded !== middle) return scanned;
|
|
11397
|
+
const survivor = secrets.get(middle);
|
|
11398
|
+
if (survivor === void 0) return scanned;
|
|
11399
|
+
if (scanned !== prefix + survivor + suffix) return scanned;
|
|
11400
|
+
return prefix + token + suffix;
|
|
11401
|
+
}
|
|
11402
|
+
/**
|
|
11135
11403
|
* Keys tried, in order, when pairing two arrays whose ORDER cannot be trusted
|
|
11136
11404
|
* (issue #1915).
|
|
11137
11405
|
*
|
|
@@ -11331,7 +11599,7 @@ function redactByPath(bag, source, secrets, rules, secretExpressions) {
|
|
|
11331
11599
|
if (!rules.sourceIsSameGeneration && isSingleDynamicReferenceToken(bag)) return secrets.get(bag) ?? bag;
|
|
11332
11600
|
return source;
|
|
11333
11601
|
}
|
|
11334
|
-
return
|
|
11602
|
+
return positionByEmbeddedSpan(bag, source, secrets);
|
|
11335
11603
|
}
|
|
11336
11604
|
if (typeof bag === "string" && isPlainObject$2(source)) {
|
|
11337
11605
|
const certified = positionByCrossStackSource(bag, source, secrets);
|
|
@@ -11449,12 +11717,13 @@ function subtreeHasDynamicReference(value) {
|
|
|
11449
11717
|
* Widening it changes one answer, in the SAFE direction for BOTH readers.
|
|
11450
11718
|
*
|
|
11451
11719
|
* `drift.ts`'s `survivingDynamicReferences` is the reader that is easy to
|
|
11452
|
-
* forget, because it lives in another file — it feeds
|
|
11453
|
-
*
|
|
11454
|
-
*
|
|
11455
|
-
*
|
|
11456
|
-
*
|
|
11457
|
-
* `
|
|
11720
|
+
* forget, because it lives in another file — it feeds the survivor REPORT
|
|
11721
|
+
* (`onUnresolved`, and through it the `unresolvedToken` cause), so seeing MORE
|
|
11722
|
+
* tokens can only report more, never less. Do not shorten this to "the only
|
|
11723
|
+
* reader": that sentence is what a later editor uses to bound the blast
|
|
11724
|
+
* radius of touching the class, and getting it wrong points them away from
|
|
11725
|
+
* the report / `--json` / `--accept` path where an unreported survivor would
|
|
11726
|
+
* surface.
|
|
11458
11727
|
*
|
|
11459
11728
|
* The other reader is the DECLARED direction for issue #1901:
|
|
11460
11729
|
* {@link mixedLeafMayCarryPublicReference}, which asks whether a MIXED leaf
|
|
@@ -11860,9 +12129,9 @@ function unkeyedArrayPairsByAnchors(bag, source) {
|
|
|
11860
12129
|
* from one the pass decided IN FAVOUR of the value already there. Two shapes
|
|
11861
12130
|
* hit it, both fabricating a baseline `cdkd drift --revert` then pushes:
|
|
11862
12131
|
*
|
|
11863
|
-
* - the resolver's unsupported-service arm leaves
|
|
11864
|
-
*
|
|
11865
|
-
* leaf. The string arm returns `source` — a decision — and the equality made
|
|
12132
|
+
* - the resolver's unsupported-service arm leaves a `{{resolve:...}}` token it
|
|
12133
|
+
* has no arm for LITERAL (`ssm-secure:` was one until issue #2482), so AWS
|
|
12134
|
+
* echoes it back and the source leaf EQUALS the bag leaf. The string arm returns `source` — a decision — and the equality made
|
|
11866
12135
|
* it look like no decision at all. (A BARE such token takes the whole-token
|
|
11867
12136
|
* arm and one embedded in text takes the mixed-leaf arm; both decide, and
|
|
11868
12137
|
* both were misread.)
|
|
@@ -12046,7 +12315,8 @@ function learnWholeTokenNeedle(collector, bag, source) {
|
|
|
12046
12315
|
* would then contain a whole `{{resolve:...}}` token and a resolved readback
|
|
12047
12316
|
* cannot end with one. The shape it genuinely decides is a second reference
|
|
12048
12317
|
* that survives LITERALLY in the readback — the resolver's
|
|
12049
|
-
* unsupported-service arm (`ssm-secure:`
|
|
12318
|
+
* unsupported-service arm produces exactly that (`ssm-secure:` did until
|
|
12319
|
+
* issue #2482; a spelling with no arm still does) — where the
|
|
12050
12320
|
* extraction would in fact be right and is declined anyway. Measured: a
|
|
12051
12321
|
* both-resolved fixture leaves this line unfenced.
|
|
12052
12322
|
* - the source's literal PREFIX and SUFFIX must both be present at the ends of
|
|
@@ -12060,17 +12330,10 @@ function learnWholeTokenNeedle(collector, bag, source) {
|
|
|
12060
12330
|
* correctly, while an `indexOf` scan would cut it short.
|
|
12061
12331
|
*/
|
|
12062
12332
|
function learnMixedLeafNeedle(collector, bag, source) {
|
|
12063
|
-
const
|
|
12064
|
-
if (
|
|
12065
|
-
const
|
|
12066
|
-
const token = source.slice(span.start, span.end);
|
|
12333
|
+
const frame = singleSpanFrame(bag, source);
|
|
12334
|
+
if (frame === void 0) return;
|
|
12335
|
+
const { token, middle: plaintext } = frame;
|
|
12067
12336
|
if (!expressionMaySeedANeedle(token)) return;
|
|
12068
|
-
const prefix = source.slice(0, span.start);
|
|
12069
|
-
const suffix = source.slice(span.end);
|
|
12070
|
-
if (bag.length <= prefix.length + suffix.length) return;
|
|
12071
|
-
if (!bag.startsWith(prefix) || !bag.endsWith(suffix)) return;
|
|
12072
|
-
const plaintext = bag.slice(prefix.length, bag.length - suffix.length);
|
|
12073
|
-
if (isSingleDynamicReferenceToken(plaintext)) return;
|
|
12074
12337
|
learnNeedle(collector, plaintext, token);
|
|
12075
12338
|
}
|
|
12076
12339
|
/**
|
|
@@ -15100,7 +15363,7 @@ async function assumeRoleForCrossAccountStateRead(roleArn) {
|
|
|
15100
15363
|
const promise = (async () => {
|
|
15101
15364
|
const logger = getLogger().child("role-arn");
|
|
15102
15365
|
logger.debug(`Assuming role for cross-account state read: ${roleArn}`);
|
|
15103
|
-
const sts = new STSClient({});
|
|
15366
|
+
const sts = new STSClient({ ...awsClientDefaults() });
|
|
15104
15367
|
try {
|
|
15105
15368
|
let response;
|
|
15106
15369
|
try {
|
|
@@ -15169,7 +15432,10 @@ async function applyRoleArnIfSet(opts) {
|
|
|
15169
15432
|
if (!roleArn) return;
|
|
15170
15433
|
const logger = getLogger().child("role-arn");
|
|
15171
15434
|
logger.debug(`Assuming role ${roleArn}...`);
|
|
15172
|
-
const sts = new STSClient({
|
|
15435
|
+
const sts = new STSClient({
|
|
15436
|
+
...awsClientDefaults(),
|
|
15437
|
+
...opts.region && { region: opts.region }
|
|
15438
|
+
});
|
|
15173
15439
|
try {
|
|
15174
15440
|
const response = await sts.send(new AssumeRoleCommand({
|
|
15175
15441
|
RoleArn: roleArn,
|
|
@@ -15323,17 +15589,22 @@ function s3BucketWebsiteUrl(bucketName, region) {
|
|
|
15323
15589
|
/**
|
|
15324
15590
|
* The `{{resolve:<service>:...}}` families whose value can be a SECRET, and
|
|
15325
15591
|
* therefore the only ones the region question below is asked about: every
|
|
15326
|
-
* `secretsmanager` reference by spelling,
|
|
15327
|
-
*
|
|
15328
|
-
*
|
|
15329
|
-
*
|
|
15330
|
-
*
|
|
15331
|
-
*
|
|
15332
|
-
*
|
|
15333
|
-
*
|
|
15334
|
-
*
|
|
15335
|
-
|
|
15336
|
-
|
|
15592
|
+
* `secretsmanager` reference by spelling, every `ssm-secure` one by spelling
|
|
15593
|
+
* (issue #2482 — it is resolved through the same `GetParameter` as `ssm`, so
|
|
15594
|
+
* the wrong region answers it in exactly the same way), and every `ssm` one,
|
|
15595
|
+
* which is secret exactly when its parameter is a `SecureString` (issue #1901).
|
|
15596
|
+
*
|
|
15597
|
+
* Every OTHER service is `local` because cdkd cannot resolve it at all — the
|
|
15598
|
+
* resolver's unsupported-service arm leaves such a token in place, so there
|
|
15599
|
+
* is no lookup for a region to get wrong. None of CloudFormation's three
|
|
15600
|
+
* services is in that position any more; the arm exists for a spelling that
|
|
15601
|
+
* is not a dynamic reference at all, or one AWS adds later.
|
|
15602
|
+
*/
|
|
15603
|
+
const REPLAY_SECRET_SERVICES = /* @__PURE__ */ new Set([
|
|
15604
|
+
"secretsmanager",
|
|
15605
|
+
"ssm",
|
|
15606
|
+
"ssm-secure"
|
|
15607
|
+
]);
|
|
15337
15608
|
/**
|
|
15338
15609
|
* Split a `{{resolve:secretsmanager:...}}` inner body into its SECRET_ID.
|
|
15339
15610
|
*
|
|
@@ -15369,7 +15640,7 @@ function secretsManagerSecretId(inner) {
|
|
|
15369
15640
|
* a different thing in the refusal message than the one that would be read.
|
|
15370
15641
|
*/
|
|
15371
15642
|
function ssmParameterName(inner) {
|
|
15372
|
-
return inner.substring(
|
|
15643
|
+
return inner.substring(inner.indexOf(":") + 1);
|
|
15373
15644
|
}
|
|
15374
15645
|
/**
|
|
15375
15646
|
* The region an ARN names, or `undefined` for anything that is not an ARN with
|
|
@@ -16470,7 +16741,10 @@ var WAFv2WebACLProvider = class {
|
|
|
16470
16741
|
"AssociationConfig"
|
|
16471
16742
|
])]]);
|
|
16472
16743
|
getClient() {
|
|
16473
|
-
if (!this.wafv2Client) this.wafv2Client = new WAFV2Client(
|
|
16744
|
+
if (!this.wafv2Client) this.wafv2Client = new WAFV2Client({
|
|
16745
|
+
...awsClientDefaults(),
|
|
16746
|
+
...this.providerRegion ? { region: this.providerRegion } : {}
|
|
16747
|
+
});
|
|
16474
16748
|
return this.wafv2Client;
|
|
16475
16749
|
}
|
|
16476
16750
|
/**
|
|
@@ -18240,6 +18514,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
18240
18514
|
const building = (async () => {
|
|
18241
18515
|
const { ServiceDiscoveryClient } = await import("@aws-sdk/client-servicediscovery");
|
|
18242
18516
|
return new ServiceDiscoveryClient({
|
|
18517
|
+
...awsClientDefaults({ profile: scoped.credentialConfig?.profile }),
|
|
18243
18518
|
...scoped.credentialConfig ?? {},
|
|
18244
18519
|
...region ? { region } : {}
|
|
18245
18520
|
});
|
|
@@ -20047,7 +20322,10 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
20047
20322
|
getCfnClient(region) {
|
|
20048
20323
|
let client = this.cfnClients[region];
|
|
20049
20324
|
if (!client) {
|
|
20050
|
-
client = new CloudFormationClient({
|
|
20325
|
+
client = new CloudFormationClient({
|
|
20326
|
+
...awsClientDefaults(),
|
|
20327
|
+
region
|
|
20328
|
+
});
|
|
20051
20329
|
this.cfnClients[region] = client;
|
|
20052
20330
|
}
|
|
20053
20331
|
return client;
|
|
@@ -20231,6 +20509,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
20231
20509
|
const { bucket, region: bucketRegion } = await resolveCrossAccountStateBucket(parsed.accountId, credentials);
|
|
20232
20510
|
const prefix = context.stateBackend?.prefix ?? "cdkd";
|
|
20233
20511
|
return new S3StateBackend(new S3Client({
|
|
20512
|
+
...awsClientDefaults(),
|
|
20234
20513
|
region: bucketRegion,
|
|
20235
20514
|
credentials: {
|
|
20236
20515
|
accessKeyId: credentials.accessKeyId,
|
|
@@ -20421,7 +20700,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
20421
20700
|
});
|
|
20422
20701
|
for (const { fullMatch, inner } of matches) {
|
|
20423
20702
|
const service = inner.split(":")[0];
|
|
20424
|
-
const isKnownSecret = service === "secretsmanager" || recordedSecretExpressions.has(fullMatch);
|
|
20703
|
+
const isKnownSecret = service === "secretsmanager" || service === "ssm-secure" || recordedSecretExpressions.has(fullMatch);
|
|
20425
20704
|
if (isKnownSecret && context?.skipDynamicReferences) continue;
|
|
20426
20705
|
const regionVerdict = classifyReplaySecretRegion(fullMatch, this.explicitRegion ?? this.resolverRegion, context?.producerRegions);
|
|
20427
20706
|
if (regionVerdict.kind === "ambiguous") throw markNonRetryable(new DynamicReferenceRegionAmbiguousError(`Refusing to resolve the secret reference ${fullMatch}: it names '${regionVerdict.secretName}' without a region, and this stack reads from ${regionVerdict.foreignProducerRegions.join(", ")} as well as its own region. cdkd cannot tell which one must answer, and resolving against the wrong one yields a different secret. Spell the reference as a full ARN to say which region owns it.`));
|
|
@@ -20432,7 +20711,10 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
20432
20711
|
}
|
|
20433
20712
|
const cached = this.cachedDynamicReferences.get(fullMatch);
|
|
20434
20713
|
if (cached) {
|
|
20435
|
-
if (cached.secret && cached.value)
|
|
20714
|
+
if (cached.secret && cached.value) {
|
|
20715
|
+
context?.recordedSecretValues?.set(cached.value, fullMatch);
|
|
20716
|
+
if (context?.recordedSecretValues) recordResolvedPair(context.recordedSecretValues, fullMatch, cached.value);
|
|
20717
|
+
}
|
|
20436
20718
|
result = result.replace(fullMatch, () => cached.value);
|
|
20437
20719
|
continue;
|
|
20438
20720
|
}
|
|
@@ -20464,6 +20746,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
20464
20746
|
if (!decrypt) continue;
|
|
20465
20747
|
}
|
|
20466
20748
|
resolved = param.value;
|
|
20749
|
+
} else if (service === "ssm-secure") {
|
|
20750
|
+
const param = await this.resolveSSMReference(parts, true, "ssm-secure");
|
|
20751
|
+
if (!param.secure) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Refusing to resolve ${fullMatch}: the parameter is a ${param.type} parameter, and the ssm-secure spelling is defined for SecureString parameters only. Reference it as {{resolve:ssm:...}} if it is public configuration.`));
|
|
20752
|
+
isSecret = true;
|
|
20753
|
+
resolved = param.value;
|
|
20467
20754
|
} else {
|
|
20468
20755
|
this.logger.warn(`Unsupported dynamic reference service: ${service}`);
|
|
20469
20756
|
continue;
|
|
@@ -20474,7 +20761,8 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
20474
20761
|
});
|
|
20475
20762
|
if (isSecret && resolved) {
|
|
20476
20763
|
context?.recordedSecretValues?.set(resolved, fullMatch);
|
|
20477
|
-
if (
|
|
20764
|
+
if (context?.recordedSecretValues) recordResolvedPair(context.recordedSecretValues, fullMatch, resolved);
|
|
20765
|
+
if (service === "secretsmanager" || service === "ssm-secure") this.pinSecretVerdict(fullMatch, true);
|
|
20478
20766
|
}
|
|
20479
20767
|
result = result.replace(fullMatch, () => resolved);
|
|
20480
20768
|
}
|
|
@@ -20684,16 +20972,16 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
20684
20972
|
* discard the value when `secure` is set — it is ciphertext, not the resolved
|
|
20685
20973
|
* reference.
|
|
20686
20974
|
*/
|
|
20687
|
-
async resolveSSMReference(parts, decrypt = true) {
|
|
20975
|
+
async resolveSSMReference(parts, decrypt = true, service = "ssm") {
|
|
20688
20976
|
const parameterName = parts.slice(1).join(":");
|
|
20689
|
-
if (!parameterName) throw new Error(
|
|
20690
|
-
this.logger.debug(`Resolving dynamic reference:
|
|
20977
|
+
if (!parameterName) throw new Error(`Dynamic reference: ${service} PARAMETER_NAME is required`);
|
|
20978
|
+
this.logger.debug(`Resolving dynamic reference: ${service}:${parameterName}`);
|
|
20691
20979
|
const client = this.clientsForRegion(this.explicitRegion).ssm;
|
|
20692
20980
|
const command = new GetParameterCommand({
|
|
20693
20981
|
Name: parameterName,
|
|
20694
20982
|
WithDecryption: decrypt
|
|
20695
20983
|
});
|
|
20696
|
-
const response = await this.sendWithThrottleRetry(() => client.send(command),
|
|
20984
|
+
const response = await this.sendWithThrottleRetry(() => client.send(command), `${service}:${parameterName}`);
|
|
20697
20985
|
const paramValue = response.Parameter?.Value;
|
|
20698
20986
|
if (paramValue === void 0 || paramValue === null) throw new Error(`Dynamic reference: SSM parameter '${parameterName}' not found or has no value`);
|
|
20699
20987
|
const paramType = response.Parameter?.Type;
|
|
@@ -20701,7 +20989,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
20701
20989
|
if (secure && paramType !== "SecureString" && !this.warnedUnrecognizedSsmTypes.has(`${parameterName}\u0000${String(paramType)}`)) {
|
|
20702
20990
|
this.warnedUnrecognizedSsmTypes.add(`${parameterName}\u0000${String(paramType)}`);
|
|
20703
20991
|
const reported = paramType === void 0 ? "(absent)" : `'${String(paramType)}'`;
|
|
20704
|
-
this.logger.warn(`SSM parameter '${parameterName}' reported an unrecognized Type ${reported} — treating its value as a secret, so cdkd will persist the {{resolve
|
|
20992
|
+
this.logger.warn(`SSM parameter '${parameterName}' reported an unrecognized Type ${reported} — treating its value as a secret, so cdkd will persist the {{resolve:${service}:...}} expression rather than the resolved value. Declare the parameter as String / StringList if it is public config.`);
|
|
20705
20993
|
}
|
|
20706
20994
|
return {
|
|
20707
20995
|
value: paramValue,
|
|
@@ -21836,7 +22124,7 @@ var CloudControlProvider = class {
|
|
|
21836
22124
|
const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
|
|
21837
22125
|
if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
|
|
21838
22126
|
this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
|
|
21839
|
-
const { ASGProvider } = await import("./asg-provider-
|
|
22127
|
+
const { ASGProvider } = await import("./asg-provider-BvIH9Ivw.js").then((n) => n.n);
|
|
21840
22128
|
return withIndeterminateGuard(await new ASGProvider().delete(logicalId, physicalId, resourceType, _properties, context), indeterminateGuard);
|
|
21841
22129
|
}
|
|
21842
22130
|
const isProtectedEc2Instance = context?.removeProtection === true && resourceType === "AWS::EC2::Instance";
|
|
@@ -22209,7 +22497,7 @@ var CloudControlProvider = class {
|
|
|
22209
22497
|
break;
|
|
22210
22498
|
case "AWS::RDS::DBCluster":
|
|
22211
22499
|
try {
|
|
22212
|
-
const cluster = (await new RDSClient({}).send(new DescribeDBClustersCommand({ DBClusterIdentifier: physicalId }))).DBClusters?.[0];
|
|
22500
|
+
const cluster = (await new RDSClient({ ...awsClientDefaults() }).send(new DescribeDBClustersCommand({ DBClusterIdentifier: physicalId }))).DBClusters?.[0];
|
|
22213
22501
|
if (cluster) {
|
|
22214
22502
|
if (cluster.Endpoint) enriched["Endpoint.Address"] = cluster.Endpoint;
|
|
22215
22503
|
if (cluster.Port !== void 0) enriched["Endpoint.Port"] = String(cluster.Port);
|
|
@@ -22224,7 +22512,7 @@ var CloudControlProvider = class {
|
|
|
22224
22512
|
break;
|
|
22225
22513
|
case "AWS::RDS::DBInstance":
|
|
22226
22514
|
try {
|
|
22227
|
-
const inst = (await new RDSClient({}).send(new DescribeDBInstancesCommand({ DBInstanceIdentifier: physicalId }))).DBInstances?.[0];
|
|
22515
|
+
const inst = (await new RDSClient({ ...awsClientDefaults() }).send(new DescribeDBInstancesCommand({ DBInstanceIdentifier: physicalId }))).DBInstances?.[0];
|
|
22228
22516
|
if (inst) {
|
|
22229
22517
|
if (inst.Endpoint?.Address) enriched["Endpoint.Address"] = inst.Endpoint.Address;
|
|
22230
22518
|
if (inst.Endpoint?.Port !== void 0) enriched["Endpoint.Port"] = String(inst.Endpoint.Port);
|
|
@@ -22378,7 +22666,7 @@ var CloudControlProvider = class {
|
|
|
22378
22666
|
break;
|
|
22379
22667
|
case "AWS::ElastiCache::ReplicationGroup":
|
|
22380
22668
|
try {
|
|
22381
|
-
const rg = (await new ElastiCacheClient({}).send(new DescribeReplicationGroupsCommand({ ReplicationGroupId: physicalId }))).ReplicationGroups?.[0];
|
|
22669
|
+
const rg = (await new ElastiCacheClient({ ...awsClientDefaults() }).send(new DescribeReplicationGroupsCommand({ ReplicationGroupId: physicalId }))).ReplicationGroups?.[0];
|
|
22382
22670
|
if (rg) {
|
|
22383
22671
|
const primaryNode = rg.NodeGroups?.[0];
|
|
22384
22672
|
if (primaryNode?.PrimaryEndpoint?.Address) enriched["PrimaryEndPoint.Address"] = primaryNode.PrimaryEndpoint.Address;
|
|
@@ -22400,7 +22688,7 @@ var CloudControlProvider = class {
|
|
|
22400
22688
|
break;
|
|
22401
22689
|
case "AWS::Redshift::Cluster":
|
|
22402
22690
|
try {
|
|
22403
|
-
const cluster = (await new RedshiftClient({}).send(new DescribeClustersCommand({ ClusterIdentifier: physicalId }))).Clusters?.[0];
|
|
22691
|
+
const cluster = (await new RedshiftClient({ ...awsClientDefaults() }).send(new DescribeClustersCommand({ ClusterIdentifier: physicalId }))).Clusters?.[0];
|
|
22404
22692
|
if (cluster?.Endpoint) {
|
|
22405
22693
|
if (cluster.Endpoint.Address) enriched["Endpoint.Address"] = cluster.Endpoint.Address;
|
|
22406
22694
|
if (cluster.Endpoint.Port !== void 0) enriched["Endpoint.Port"] = String(cluster.Endpoint.Port);
|
|
@@ -22412,7 +22700,7 @@ var CloudControlProvider = class {
|
|
|
22412
22700
|
break;
|
|
22413
22701
|
case "AWS::OpenSearchService::Domain":
|
|
22414
22702
|
try {
|
|
22415
|
-
const domain = (await new OpenSearchClient({}).send(new DescribeDomainCommand({ DomainName: physicalId }))).DomainStatus;
|
|
22703
|
+
const domain = (await new OpenSearchClient({ ...awsClientDefaults() }).send(new DescribeDomainCommand({ DomainName: physicalId }))).DomainStatus;
|
|
22416
22704
|
if (domain) {
|
|
22417
22705
|
const endpoint = domain.Endpoint ?? domain.Endpoints?.["vpc"];
|
|
22418
22706
|
if (endpoint) enriched["DomainEndpoint"] = endpoint;
|
|
@@ -33940,7 +34228,10 @@ var DeployEngine = class {
|
|
|
33940
34228
|
}
|
|
33941
34229
|
outputsPassCompleted = true;
|
|
33942
34230
|
} finally {
|
|
33943
|
-
if (context.recordedSecretValues)
|
|
34231
|
+
if (context.recordedSecretValues) {
|
|
34232
|
+
for (const [value, expr] of context.recordedSecretValues) this.outputSecrets.set(value, expr);
|
|
34233
|
+
mergeResolvedPairs(context.recordedSecretValues, this.outputSecrets);
|
|
34234
|
+
}
|
|
33944
34235
|
if (!outputsPassCompleted) this.outputsSourceUsable = false;
|
|
33945
34236
|
}
|
|
33946
34237
|
for (const [outputKey, output] of Object.entries(template.Outputs)) {
|
|
@@ -33961,5 +34252,5 @@ var DeployEngine = class {
|
|
|
33961
34252
|
};
|
|
33962
34253
|
|
|
33963
34254
|
//#endregion
|
|
33964
|
-
export { DEFAULT_STATE_PREFIX as $, resolveUseCdkBootstrapAssets as $n,
|
|
33965
|
-
//# sourceMappingURL=deploy-engine-
|
|
34255
|
+
export { DEFAULT_STATE_PREFIX as $, resolveUseCdkBootstrapAssets as $n, retryClassificationText as $r, isSingleDynamicReferenceToken as $t, bold as A, validateAssetBucketName as An, LocalMigrateError as Ar, requireConfigObject as At, secretBearingStateKeyWarning as B, runDockerStreaming as Bn, StackTerminationProtectionError as Br, DiffCalculator as Bt, unsupportedFinalSnapshotError as C, BOOTSTRAP_MARKER_PREFIX as Cn, ConfigError as Cr, assertRegionMatch as Ct, isStatefulRecreateTargetSync as D, isCrossRegionRedirect as Dn, DynamicReferenceRegionAmbiguousError as Dr, readConfigString as Dt, MULTI_REGION_RECREATE_BLOCKED_TYPES as E, getBootstrapMarkerKey as En, DeployCancelledError as Er, configStringRefusal as Et, yellow as F, dockerSpawnEnvWithSensitive as Fn, PartialFailureError as Fr, s3BucketDomainName as Ft, clearOnUpdateRemoval as G, getDefaultStateBucketName as Gn, normalizeAwsError as Gr, TemplateParser as Gt, getCurrentResourceSecrets as H, getDockerImageBySourceHash as Hn, SynthesisError as Hr, describeTypeWithThrottleRetry as Ht, collectDeclaredOutputNames as I, formatDockerLoginError as In, ProvisioningError as Ir, s3BucketDualStackDomainName as It, findSilentDropProperties as J, resolveAutoAssetStorage as Jn, isRetryableTransientError as Jr, TEMPLATE_SOURCED_RULES as Jt, ProviderRegistry as K, getLegacyStateBucketName as Kn, withErrorHandling as Kr, STATE_SOURCED_CROSS_GENERATION_RULES as Kt, collectPublishedOutputNames as L, getDockerCmd as Ln, ResourceTimeoutError as Lr, s3BucketRegionalDomainName as Lt, gray as M, buildDenyExternalAccessPolicy as Mn, LockError as Mr, classifyReplaySecretRegion as Mt, green as N, describeAwsFailure as Nn, MissingCdkCliError as Nr, producerRegionsFromState as Nt, renderStatefulReason as O, parseBootstrapMarker as On, IntrinsicResolutionRefusalError as Or, replayWarn as Ot, red as P, buildDockerImage as Pn, NestedStackChildDirectDestroyError as Pr, s3BucketArn as Pt, CUSTOM_RESOURCE_RESPONSE_PREFIX as Q, resolveStateBucketWithDefaultAndSource as Qn, markRedactedCause as Qr, errorCauseChain as Qt, exportAliasCollisionScrubWarning as R, partitionSensitiveEnv as Rn, ResourceUpdateNotSupportedError as Rr, s3BucketWebsiteUrl as Rt, refusesFinalSnapshot as S, AssetModeResolver as Sn, CdkdError as Sr, resolveExplicitPhysicalId as St, extractDeploymentEventError as T, ensureAssetStorage as Tn, DependencyError as Tr, configBooleanRefusal as Tt, IAMRoleProvider as U, Synthesizer as Un, formatError as Ur, withRetry as Ut, stateKeySecretExposure as V, AssetManifestLoader as Vn, StateError as Vr, INTRINSIC_KEYS as Vt, collectInlinePolicyNamesManagedBySiblings as W, synthesisStatusMessage as Wn, isCdkdError as Wr, DagBuilder as Wt, maskDeep as X, resolveSkipPrefix as Xn, isTransientServerError as Xr, createSecretMasker as Xt, createMaskedRetryLogger as Y, resolveCaptureObservedState as Yn, isThrottlingError as Yr, carriesSecretMask as Yt, maskerOrIdentity as Z, resolveStateBucketWithDefault as Zn, markNonRetryable as Zr, dynamicReferenceTokens as Zt, PRE_DELETE_SNAPSHOT_TYPES as _, createAssetRedirectResolver as _n, AwsClients as _r, isUnboundTemplateParameter as _t, DeploymentEventsStore as a, scrubResourceRecord as an, findLargeInlineResources as ar, CloudControlProvider as at, createPreDeleteFinalSnapshot as b, escapeRegExp$1 as bn, setAwsClients as br, WAFv2WebACLProvider as bt, replayFailedOperations as c, rebuildClientForBucketRegion as cn, displaySafe as cr, deleteIndeterminateGuards as ct, updatePartialReason as d, importableOutputs as dn, canonicalizeRegion as dr, isTerminationProtectionPropagationError as dt, __exportAll as ei, maskSecretsInError as en, stateBucketExistenceConfirmed as er, beginCommandInterruptScope as et, withResourceDeadline as f, shouldRetainResource as fn, derivePartitionAndUrlSuffix as fr, IntrinsicFunctionResolver as ft, ATOMIC_FINAL_SNAPSHOT_TYPES as g, buildAssetRedirectMap as gn, resolveBucketRegion as gr, getAccountInfo as gt, computeImplicitDeleteEdges as h, WorkGraph as hn, clearBucketRegionCache as hr, coerceParameterTypedValue as ht, DeploymentEventsReader as i, redactSecretsForState as in, MIGRATE_TMP_PREFIX as ir, startInterruptWatch as it, cyan as j, validateContainerRepoName as jn, LocalStartServiceError as jr, requireConfigString as jt, formatResourceLine as k, readBootstrapMarkerBody as kn, LocalInvokeBuildError as kr, requireConfigArray as kt, replayRollback as l, exportNamesCarriedFrom as ln, expectedOwnerParam as lr, deleteSkipReason as lt, IMPLICIT_DELETE_DEPENDENCIES as m, stringifyValue as mn, processStackMessages as mr, cfnRefValueFromPhysicalId as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, recordMaskOnlyValue as nn, CFN_TEMPLATE_BODY_LIMIT as nr, interruptWatchListenerCount as nt, planFailedOps as o, LockManager as on, uploadCfnTemplate as or, slowCcOperationTimeoutMs as ot, maskingRetryLogger as p, AssetPublisher as pn, AssemblyReader as pr, carriesDynamicReference as pt, findActionableSilentDrops as q, resolveApp as qn, isMarkedNonRetryable as qr, STATE_SOURCED_READBACK_RULES as qt, DeployEngine as r, recoverMaskedOutput as rn, CFN_TEMPLATE_URL_LIMIT as rr, isInterruptedWaitError as rt, planRollback as s, S3StateBackend as sn, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as sr, UNSPECIFIED_SKIP_REASON as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, maskSecretsInText as tn, warnDeprecatedNoPrefixCliFlag as tr, endCommandInterruptScope as tt, updatePartialMessage as u, importableOutputKeys as un, PARTITION_TABLE as ur, disableInstanceApiTermination as ut, buildFinalSnapshotIdentifier as v, loadPublishableAssetManifest as vn, getAwsClients as vr, parameterTypeMayLoseSecretIdentity as vt, makeCanonicalizePropertiesFn as w, assertAssetBucketRegion as wn, CrossAccountSecretRefusalError as wr, coerceCfnBoolean as wt, isFinalSnapshotError as x, stripControlChars as xn, AssetError as xr, normalizeAwsTagsToCfn as xt, ccRoutedFinalSnapshotError as y, rewriteTemplateAssetReferences as yn, resetAwsClients as yr, refStateLookupFromResource as yt, isExportAliasCollision as z, runDockerForeground as zn, StackHasActiveImportsError as zr, applyRoleArnIfSet as zt };
|
|
34256
|
+
//# sourceMappingURL=deploy-engine-CA50haPO.js.map
|