cdk-local 0.149.5 → 0.149.7

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.
@@ -1,4 +1,4 @@
1
- import { C as resolveConfiguredLogLevel, E as setEmbedConfig, S as getLogger, _ as describeCredentialLoadFailure, a as runDockerForeground, b as sanitizeServiceExceptionMessage, c as warnFinchArgvExposure, d as displayUntrustedValue, f as namesTheSameDirectory, g as describeAwsFailureForWarn, h as clampErrorName, i as getDockerCmd, l as absoluteAssemblyPathEscape, m as resolveAssemblyPath, n as finchSecretArgvRefusal, o as runDockerStreaming, p as renderAssemblyPathEscape, r as formatDockerLoginError, s as spawnStreaming, u as assemblyPathEscape, v as flattenToOneLine, w as getEmbedConfig, x as stringifyThrown, y as isAwsServiceException } from "./docker-cmd-B5yU__zC.js";
1
+ import { C as stringifyThrown, E as getEmbedConfig, O as setEmbedConfig, S as sanitizeServiceExceptionMessage, T as resolveConfiguredLogLevel, _ as clampErrorName, a as isDockerClientEnvKey, b as flattenToOneLine, c as runDockerStreaming, d as absoluteAssemblyPathEscape, f as assemblyPathEscape, g as resolveAssemblyPath, h as renderAssemblyPathEscape, i as getDockerCmd, l as spawnStreaming, m as namesTheSameDirectory, n as finchSecretArgvRefusal, o as isMalformedEnvKey, p as displayUntrustedValue, r as formatDockerLoginError, s as runDockerForeground, u as warnFinchArgvExposure, v as describeAwsFailureForWarn, w as getLogger, x as isAwsServiceException, y as describeCredentialLoadFailure } from "./docker-cmd-leyvDn1u.js";
2
2
  import { chmodSync, cpSync, createWriteStream, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, readlinkSync, realpathSync, rmSync, statSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
3
3
  import { homedir, tmpdir } from "node:os";
4
4
  import * as path$1 from "node:path";
@@ -6358,6 +6358,36 @@ function toFlagName(field) {
6358
6358
  return "--" + field.replace(/([A-Z])/g, "-$1").toLowerCase();
6359
6359
  }
6360
6360
 
6361
+ //#endregion
6362
+ //#region src/utils/own-keys.ts
6363
+ /**
6364
+ * Write `key` as an OWN, ordinary data property of `target`.
6365
+ *
6366
+ * Plain assignment (`target[key] = value`) on an object literal routes the
6367
+ * key `__proto__` through `Object.prototype`'s `__proto__` accessor: a string
6368
+ * value is silently ignored and an object value REPLACES the target's
6369
+ * prototype. For maps keyed by user-supplied names (env vars, secrets) that
6370
+ * means a variable literally named `__proto__` vanishes without a warning
6371
+ * (issue #769). `Object.defineProperty` never consults the accessor, so the
6372
+ * key lands as an own property like any other.
6373
+ *
6374
+ * The descriptor matches what an ordinary assignment creates (writable,
6375
+ * enumerable, configurable), so `Object.keys` / `Object.entries` / spread /
6376
+ * `JSON.stringify` / `delete` treat the key exactly like its siblings.
6377
+ *
6378
+ * Readers of such a map must test membership with `Object.hasOwn`, never
6379
+ * `in` or a bare `map[key] !== undefined`: both see the inherited
6380
+ * `__proto__` accessor on a map that does not carry the key.
6381
+ */
6382
+ function defineOwnKey(target, key, value) {
6383
+ Object.defineProperty(target, key, {
6384
+ value,
6385
+ writable: true,
6386
+ enumerable: true,
6387
+ configurable: true
6388
+ });
6389
+ }
6390
+
6361
6391
  //#endregion
6362
6392
  //#region src/local/state-resolver.ts
6363
6393
  /**
@@ -7020,12 +7050,12 @@ function substituteEnvVarsFromState(templateEnv, contextOrResources) {
7020
7050
  const context = isContext(contextOrResources) ? contextOrResources : { resources: contextOrResources };
7021
7051
  for (const [key, value] of Object.entries(templateEnv)) {
7022
7052
  if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
7023
- env[key] = value;
7053
+ defineOwnKey(env, key, value);
7024
7054
  continue;
7025
7055
  }
7026
7056
  const { result, consumedSensitive } = resolveWithSensitivity(value, context);
7027
7057
  if (result.kind === "literal") {
7028
- env[key] = result.value;
7058
+ defineOwnKey(env, key, result.value);
7029
7059
  audit.resolvedKeys.push(key);
7030
7060
  if (consumedSensitive) audit.sensitiveKeys.push(key);
7031
7061
  } else audit.unresolved.push({
@@ -7113,12 +7143,12 @@ async function substituteEnvVarsFromStateAsync(templateEnv, contextOrResources)
7113
7143
  const context = isContext(contextOrResources) ? contextOrResources : { resources: contextOrResources };
7114
7144
  for (const [key, value] of Object.entries(templateEnv)) {
7115
7145
  if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
7116
- env[key] = value;
7146
+ defineOwnKey(env, key, value);
7117
7147
  continue;
7118
7148
  }
7119
7149
  const { result, consumedSensitive } = await resolveWithSensitivityAsync(value, context);
7120
7150
  if (result.kind === "literal") {
7121
- env[key] = result.value;
7151
+ defineOwnKey(env, key, result.value);
7122
7152
  audit.resolvedKeys.push(key);
7123
7153
  if (consumedSensitive) audit.sensitiveKeys.push(key);
7124
7154
  } else audit.unresolved.push({
@@ -7162,9 +7192,9 @@ function applyDeployedEnvFallback(resolvedEnv, unresolved, deployedEnv) {
7162
7192
  const filled = [];
7163
7193
  const stillUnresolved = [];
7164
7194
  for (const item of unresolved) {
7165
- const deployedValue = deployedEnv[item.key];
7195
+ const deployedValue = Object.hasOwn(deployedEnv, item.key) ? deployedEnv[item.key] : void 0;
7166
7196
  if (deployedValue !== void 0) {
7167
- env[item.key] = deployedValue;
7197
+ defineOwnKey(env, item.key, deployedValue);
7168
7198
  filled.push(item.key);
7169
7199
  } else stillUnresolved.push({
7170
7200
  key: item.key,
@@ -7178,6 +7208,166 @@ function applyDeployedEnvFallback(resolvedEnv, unresolved, deployedEnv) {
7178
7208
  };
7179
7209
  }
7180
7210
 
7211
+ //#endregion
7212
+ //#region src/local/ecr-uri.ts
7213
+ /**
7214
+ * Every ECR private-registry host form, read off the AWS-published `ecr`
7215
+ * endpoint list (https://docs.aws.amazon.com/general/latest/gr/ecr.html):
7216
+ *
7217
+ * - `<acct>.dkr.ecr.<region>.<partition suffix>` — the plain IPv4 endpoint.
7218
+ * - `<acct>.dkr.ecr-fips.<region>.<partition suffix>` — FIPS IPv4.
7219
+ * - `<acct>.dkr-ecr.<region>.on.aws` — dual-stack (IPv4 + IPv6).
7220
+ * - `<acct>.dkr-ecr-fips.<region>.on.aws` — dual-stack FIPS.
7221
+ *
7222
+ * Every form except the plain one is scoped to the `aws` and `aws-us-gov`
7223
+ * partitions, the only ones that endpoint list shows it in: both FIPS forms are
7224
+ * listed for `us-east-1` / `us-east-2` / `us-west-1` / `us-west-2` /
7225
+ * `us-gov-east-1` / `us-gov-west-1` only, and `on.aws` is the dual-stack DNS of
7226
+ * those two partitions (the others serve dual-stack under their own suffixes,
7227
+ * which are not listed here). A FIPS or dual-stack host in any other partition
7228
+ * is therefore not recognized and is pulled as a public image, as before issue
7229
+ * #760. The scope is by PARTITION, not by region: a commercial region AWS adds
7230
+ * later is accepted, and a login to an unserved region's FIPS host only fails,
7231
+ * because the suffix is still AWS-owned.
7232
+ */
7233
+ const FIPS_AND_DUAL_STACK_PARTITIONS = ["aws", "aws-us-gov"];
7234
+ const ECR_REGISTRY_HOST_FORMS = [
7235
+ { labels: "dkr.ecr" },
7236
+ {
7237
+ labels: "dkr.ecr-fips",
7238
+ partitions: FIPS_AND_DUAL_STACK_PARTITIONS
7239
+ },
7240
+ {
7241
+ labels: "dkr-ecr",
7242
+ fixedUrlSuffix: "on.aws",
7243
+ partitions: FIPS_AND_DUAL_STACK_PARTITIONS
7244
+ },
7245
+ {
7246
+ labels: "dkr-ecr-fips",
7247
+ fixedUrlSuffix: "on.aws",
7248
+ partitions: FIPS_AND_DUAL_STACK_PARTITIONS
7249
+ }
7250
+ ];
7251
+ const ECR_HOST_FORM_BY_LABELS = new Map(ECR_REGISTRY_HOST_FORMS.map((f) => [f.labels, f]));
7252
+ function escapeRegExp(value) {
7253
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7254
+ }
7255
+ /**
7256
+ * `<acct>.<labels>.<region>.<suffix>/` — ANCHORED at the start, so nothing
7257
+ * (userinfo, a scheme, another host) can precede the account id, and ending at
7258
+ * the first `/`, so the captured suffix is the rest of the host.
7259
+ *
7260
+ * The labels alternation is built from {@link ECR_REGISTRY_HOST_FORMS},
7261
+ * ESCAPED (an unescaped `dkr.ecr` would match `dkrxecr`) and longest first, so
7262
+ * `dkr.ecr-fips` is tried before its own `dkr.ecr` prefix. The `i` flag makes
7263
+ * the labels case-insensitive, as DNS is; it is non-unicode, so it does NOT
7264
+ * treat U+212A (Kelvin sign) as `k`, and `\d` is ASCII-only.
7265
+ */
7266
+ const ECR_URI_HOST_REGEX = new RegExp(`^(\\d{12})\\.(${[...ECR_REGISTRY_HOST_FORMS].sort((a, b) => b.labels.length - a.labels.length).map((f) => escapeRegExp(f.labels)).join("|")})\\.([^./]+)\\.([^/]+)/`, "i");
7267
+ /**
7268
+ * A region segment is ASCII alphanumerics plus `-`. Tested on the RAW capture,
7269
+ * BEFORE any case fold: `String.prototype.toLowerCase` folds U+212A onto an
7270
+ * ASCII `k`, so testing a folded value would admit `us-eKst-1` (Kelvin sign) as
7271
+ * the region `us-ekst-1` — a region the host does not name, which then seeds
7272
+ * the ECR client and the login endpoint. Today {@link REGION_SHAPE} also
7273
+ * refuses that input, because the fold is ASCII-only and the shape is ASCII
7274
+ * only, so this guard is DEFENSE IN DEPTH: it keeps the region safe if the fold
7275
+ * is ever widened to full Unicode folding.
7276
+ */
7277
+ const CANONICAL_REGION_SEGMENT = /^[A-Za-z0-9][A-Za-z0-9-]*$/;
7278
+ /**
7279
+ * The SHAPE of an AWS region id, tested on the FOLDED region after the charset
7280
+ * guard above. One alternative per partition, transcribed from the
7281
+ * `regionRegex` of each partition in the AWS SDK / botocore `partitions.json`
7282
+ * (`\w+` written as `[a-z0-9]+`):
7283
+ *
7284
+ * - aws: `(us|eu|ap|sa|ca|me|af|il|mx)-X-N`
7285
+ * - aws-cn: `cn-X-N`; aws-us-gov: `us-gov-X-N`
7286
+ * - aws-iso / -b / -e / -f: `us-iso-X-N`, `us-isob-X-N`, `eu-isoe-X-N`,
7287
+ * `us-isof-X-N`
7288
+ * - aws-eusc: `eusc-<cc>-X-N` — broader than upstream's `eusc-(de)-`, matching
7289
+ * `PARTITION_TABLE`'s `eusc-` prefix so a future country is not refused here
7290
+ * while the partition lookup accepts it.
7291
+ *
7292
+ * It refuses a label that is not a region — `s3`, `lambda-url`,
7293
+ * `s3-external-1`, `fips-us-east-1` — so `<acct>.dkr.ecr.s3.amazonaws.com` or
7294
+ * `<acct>.dkr-ecr.lambda-url.on.aws` never becomes a login target. Those hosts
7295
+ * are AWS-owned, but they are not registries, and only TLS would stop a token
7296
+ * sent there (an `insecure-registries` daemon setting turns that off).
7297
+ */
7298
+ const REGION_SHAPE = /^(?:(?:us|eu|ap|sa|ca|me|af|il|mx)-[a-z0-9]+-[0-9]+|cn-[a-z0-9]+-[0-9]+|us-gov-[a-z0-9]+-[0-9]+|us-iso-[a-z0-9]+-[0-9]+|us-isob-[a-z0-9]+-[0-9]+|eu-isoe-[a-z0-9]+-[0-9]+|us-isof-[a-z0-9]+-[0-9]+|eusc-[a-z]+-[a-z0-9]+-[0-9]+)$/;
7299
+ /**
7300
+ * A URL suffix is a dotted name of ASCII alphanumerics and `-`, tested on the
7301
+ * raw capture like the region. Today it is DEFENSE IN DEPTH, not the load-
7302
+ * bearing check: the fold below is ASCII-only, so `cloud.adc-e.uK` (Kelvin
7303
+ * sign) never becomes the `aws-iso-e` suffix, and the exact-suffix comparison
7304
+ * already refuses a port (`:`), userinfo (`@`) or any look-alike. It keeps
7305
+ * the suffix safe if that fold is ever widened to full Unicode folding.
7306
+ */
7307
+ const CANONICAL_URL_SUFFIX = /^[A-Za-z0-9][A-Za-z0-9.-]*$/;
7308
+ /**
7309
+ * Lower-case ONLY `A`-`Z`. DNS case-insensitivity is ASCII-only, and a full
7310
+ * `toLowerCase` would fold U+212A onto `k` and hand the charset guards above an
7311
+ * already-ASCII string, defeating them.
7312
+ */
7313
+ function foldAsciiUpperCase(value) {
7314
+ return value.replace(/[A-Z]+/g, (run) => run.toLowerCase());
7315
+ }
7316
+ /** Docker's rule for "component 1 is a registry host, not a repository segment". */
7317
+ function isRegistryHostComponent(component) {
7318
+ return component.includes(".") || component.includes(":") || component === "localhost";
7319
+ }
7320
+ /**
7321
+ * Lower-case the REGISTRY HOST of an image reference (ASCII letters only) and
7322
+ * leave the repository path and tag byte-identical.
7323
+ *
7324
+ * Docker's credential store is keyed on the hostname VERBATIM, so a login to
7325
+ * one spelling and a pull from another sends no credentials; every
7326
+ * docker-facing use of an ECR reference goes through this one spelling. The
7327
+ * path is not folded: docker requires it lower-case already, and rewriting it
7328
+ * would name a different image. A first component that is not a registry host
7329
+ * (`MyOrg/MyRepo:tag` is a Docker Hub path) is left alone for the same reason.
7330
+ */
7331
+ function canonicalizeImageUriHost(imageUri) {
7332
+ const slash = imageUri.indexOf("/");
7333
+ if (slash < 0) return imageUri;
7334
+ const host = imageUri.slice(0, slash);
7335
+ if (!isRegistryHostComponent(host)) return imageUri;
7336
+ return foldAsciiUpperCase(host) + imageUri.slice(slash);
7337
+ }
7338
+ /**
7339
+ * The ECR registry an image reference names, or `undefined` when its host is
7340
+ * not one of {@link ECR_REGISTRY_HOST_FORMS} with the suffix that form carries
7341
+ * for the region it names.
7342
+ *
7343
+ * The suffix check is what keeps a `docker login` from being pointed at a host
7344
+ * cdk-local does not own: `<acct>.dkr.ecr.<region>.example.com`, a look-alike
7345
+ * `amazonaws.com.evil.example`, a port or userinfo after the suffix, and a
7346
+ * form/suffix mispairing (`dkr-ecr` + `amazonaws.com`, `dkr.ecr` + `on.aws`,
7347
+ * an ISO region + `amazonaws.com`) are all refused.
7348
+ */
7349
+ function parseEcrRegistryHost(imageUri) {
7350
+ const m = ECR_URI_HOST_REGEX.exec(imageUri);
7351
+ if (!m) return void 0;
7352
+ const [, accountId, rawLabels, rawRegion, rawSuffix] = m;
7353
+ if (!CANONICAL_REGION_SEGMENT.test(rawRegion)) return void 0;
7354
+ if (!CANONICAL_URL_SUFFIX.test(rawSuffix)) return void 0;
7355
+ const form = ECR_HOST_FORM_BY_LABELS.get(foldAsciiUpperCase(rawLabels));
7356
+ if (!form) return void 0;
7357
+ const region = foldAsciiUpperCase(rawRegion);
7358
+ if (!REGION_SHAPE.test(region)) return void 0;
7359
+ const suffix = foldAsciiUpperCase(rawSuffix);
7360
+ const partition = derivePartitionAndUrlSuffix(region);
7361
+ if (form.partitions && !form.partitions.includes(partition.partition)) return void 0;
7362
+ if (suffix !== (form.fixedUrlSuffix ?? partition.urlSuffix)) return void 0;
7363
+ return {
7364
+ accountId,
7365
+ region,
7366
+ registryHost: `${accountId}.${form.labels}.${region}.${suffix}`,
7367
+ form
7368
+ };
7369
+ }
7370
+
7181
7371
  //#endregion
7182
7372
  //#region src/local/ecs-task-resolver.ts
7183
7373
  var EcsTaskResolutionError = class EcsTaskResolutionError extends Error {
@@ -7523,13 +7713,13 @@ function parseContainerDefinition(raw, idx, taskLogicalId, resources, stack, con
7523
7713
  const value = e["Value"];
7524
7714
  if (!key) continue;
7525
7715
  if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
7526
- environment[key] = String(value);
7716
+ defineOwnKey(environment, key, String(value));
7527
7717
  continue;
7528
7718
  }
7529
7719
  if (subContext) {
7530
7720
  const { result: sub, consumedSensitive } = resolveWithSensitivity(value, subContext);
7531
7721
  if (sub.kind === "literal") {
7532
- environment[key] = String(sub.value);
7722
+ defineOwnKey(environment, key, String(sub.value));
7533
7723
  if (consumedSensitive) sensitiveEnvKeys.push(key);
7534
7724
  continue;
7535
7725
  }
@@ -7692,9 +7882,10 @@ function buildSubstitutionContextFromImageContext(context) {
7692
7882
  * Parse the `Image` field of an ECS container definition.
7693
7883
  *
7694
7884
  * Three shapes:
7695
- * - `<account>.dkr.ecr.<region>.amazonaws.com/<repo>:<tag>` — same-account
7696
- * same-region ECR. Cross-account/region is hard-errored (matches
7697
- * `cdkl invoke`'s ECR-pull semantics).
7885
+ * - `<account>.dkr.ecr.<region>.amazonaws.com/<repo>:<tag>` — ECR, in any
7886
+ * host form `ECR_REGISTRY_HOST_FORMS` lists (FIPS / dual-stack too) and any
7887
+ * partition. Pulled through `pullEcrImage` (cross-account / cross-region
7888
+ * below).
7698
7889
  * - `Fn::Sub` / `Fn::Join` / `Ref` referencing a `Code.fromAsset`-style
7699
7890
  * CDK asset image. Surfaces `kind: 'cdk-asset'` with the optional
7700
7891
  * asset hash so the runner can route through `docker-build.ts`.
@@ -7741,12 +7932,12 @@ function parseContainerImage(raw, containerName, taskLogicalId, resources, _stac
7741
7932
  if (substituted.includes("AWS::")) throw new EcsTaskResolutionError(`Container '${containerName}' in task '${taskLogicalId}' has an Image that references AWS pseudo parameters (${substituted}). ${getEmbedConfig().productName} could not resolve them: confirm AWS credentials are configured so STS GetCallerIdentity succeeds, and that --region / AWS_REGION names the target region. Workaround: build the image locally (ContainerImage.fromAsset) or pin a public image.`);
7742
7933
  throw new EcsTaskResolutionError(`Container '${containerName}' in task '${taskLogicalId}' has an Image with unresolved \${...} placeholders (${substituted}). ${getEmbedConfig().cliName} only resolves AWS pseudo parameters and same-stack AWS::ECR::Repository refs.`);
7743
7934
  }
7744
- const ecrMatch = /^(\d{12})\.dkr\.ecr\.([^.]+)\.amazonaws\.com(?:\.cn)?\//.exec(substituted);
7745
- if (ecrMatch) return {
7935
+ const ecrHost = parseEcrRegistryHost(substituted);
7936
+ if (ecrHost) return {
7746
7937
  kind: "ecr",
7747
7938
  uri: substituted,
7748
- account: ecrMatch[1],
7749
- region: ecrMatch[2]
7939
+ account: ecrHost.accountId,
7940
+ region: ecrHost.region
7750
7941
  };
7751
7942
  return {
7752
7943
  kind: "public",
@@ -7782,12 +7973,12 @@ function classifyResolvedImage(uri) {
7782
7973
  if (hashMatch) out.assetHash = hashMatch[1];
7783
7974
  return out;
7784
7975
  }
7785
- const ecrMatch = /^(\d{12})\.dkr\.ecr\.([^.]+)\.amazonaws\.com(?:\.cn)?\//.exec(uri);
7786
- if (ecrMatch) return {
7976
+ const ecrHost = parseEcrRegistryHost(uri);
7977
+ if (ecrHost) return {
7787
7978
  kind: "ecr",
7788
7979
  uri,
7789
- account: ecrMatch[1],
7790
- region: ecrMatch[2]
7980
+ account: ecrHost.accountId,
7981
+ region: ecrHost.region
7791
7982
  };
7792
7983
  return {
7793
7984
  kind: "public",
@@ -8046,11 +8237,11 @@ async function applyCrossStackResolverToTask(task, context) {
8046
8237
  const value = e["Value"];
8047
8238
  if (!key) continue;
8048
8239
  if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") continue;
8049
- if (key in container.environment) continue;
8240
+ if (Object.hasOwn(container.environment, key)) continue;
8050
8241
  if (!isCrossStackIntrinsic(value)) continue;
8051
8242
  const { result: sub, consumedSensitive } = await resolveWithSensitivityAsync(value, context);
8052
8243
  if (sub.kind === "literal") {
8053
- container.environment[key] = String(sub.value);
8244
+ defineOwnKey(container.environment, key, String(sub.value));
8054
8245
  resolvedEnvKeys.add(key);
8055
8246
  if (consumedSensitive && !container.sensitiveEnvKeys.includes(key)) container.sensitiveEnvKeys.push(key);
8056
8247
  }
@@ -8805,49 +8996,27 @@ function buildDockerBuildCommand(source, tag, platformOverride) {
8805
8996
  //#endregion
8806
8997
  //#region src/local/ecr-puller.ts
8807
8998
  /**
8808
- * ECR pull fallback for `cdkl invoke` / `cdkl start-api` /
8809
- * `cdkl run-task`. When the image URI resolves to an ECR repo but
8810
- * doesn't match any cdk.out asset (typical when invoking a stack
8811
- * deployed elsewhere or sharing a centralized registry), cdk-local
8812
- * authenticates against the target registry and runs `docker pull`.
8813
- *
8814
- * **Cross-account / cross-region** (#455):
8815
- * - Same-account, same-region: fast path. No STS hop. The default
8816
- * credential chain is used directly for `ecr:GetAuthorizationToken`.
8817
- * - `ecrRoleArn` is provided: `sts:AssumeRole` is issued via the
8818
- * default credential chain to obtain temporary credentials for the
8819
- * target account. The resulting credentials authenticate the ECR
8820
- * client (regardless of region — the ECR client is built for the
8821
- * URI's region, which can differ from the caller's profile region).
8822
- * - Cross-account, NO `ecrRoleArn`: cdk-local falls through to the
8823
- * default credential chain. This works when the caller has been
8824
- * granted cross-account `ecr:GetAuthorizationToken` +
8825
- * `ecr:BatchGetImage` permissions on the target repository via an
8826
- * IAM policy; otherwise AWS rejects the call with `AccessDenied`
8827
- * and the user is pointed at `--ecr-role-arn`.
8828
- *
8829
- * The `--no-pull` semantics (C3 in the design doc):
8830
- * - When NOT set: `ecrLogin` + `docker pull <uri>`.
8831
- * - When set: skip `docker pull`. If the image isn't in the local
8832
- * cache, the subsequent `docker run` will fail; we surface a clearer
8833
- * "image not in local cache" error here so the user knows to drop
8834
- * `--no-pull` or pre-pull manually.
8835
- */
8836
- /** Regex matching the `<acct>.dkr.ecr.<region>.amazonaws.com/<repo>:<tag>` shape. */
8837
- const ECR_URI_REGEX = /^(\d{12})\.dkr\.ecr\.([^.]+)\.amazonaws\.com(?:\.cn)?\/([^:]+):(.+)$/;
8838
- /**
8839
8999
  * Parse an ECR image URI. Returns `undefined` for non-ECR URIs (typically:
8840
9000
  * Docker Hub, public.ecr.aws, gcr.io, ...) — those are user-managed
8841
9001
  * images we don't try to authenticate against.
9002
+ *
9003
+ * Recognizes every host form in `ECR_REGISTRY_HOST_FORMS` (plain, FIPS,
9004
+ * dual-stack, dual-stack FIPS) in every partition, each with the suffix that
9005
+ * form carries for its region (issue #760) — see `parseEcrRegistryHost`.
8842
9006
  */
8843
9007
  function parseEcrUri(imageUri) {
8844
- const m = ECR_URI_REGEX.exec(imageUri);
9008
+ const host = parseEcrRegistryHost(imageUri);
9009
+ if (!host) return void 0;
9010
+ const canonicalUri = canonicalizeImageUriHost(imageUri);
9011
+ const m = /^([^:]+):(.+)$/.exec(canonicalUri.slice(canonicalUri.indexOf("/") + 1));
8845
9012
  if (!m) return void 0;
8846
9013
  return {
8847
- accountId: m[1],
8848
- region: m[2],
8849
- repository: m[3],
8850
- tag: m[4]
9014
+ accountId: host.accountId,
9015
+ region: host.region,
9016
+ repository: m[1],
9017
+ tag: m[2],
9018
+ canonicalUri,
9019
+ registryHost: host.registryHost
8851
9020
  };
8852
9021
  }
8853
9022
  /**
@@ -8887,17 +9056,20 @@ function isCredentialFresh(creds) {
8887
9056
  *
8888
9057
  * Auto-detects cross-account from `STS:GetCallerIdentity` and assumes
8889
9058
  * the supplied role when set. Returns the image URI the caller should
8890
- * pass to `docker run` (same as the input — no rewriting).
9059
+ * pass to `docker run`: the input with its registry host ASCII-lower-cased
9060
+ * (`ParsedEcrUri.canonicalUri`), so the run names the same spelling the pull
9061
+ * and the login used; the repository path and tag are unchanged.
8891
9062
  */
8892
9063
  async function pullEcrImage(imageUri, options) {
8893
9064
  const logger = getLogger().child("ecr-puller");
8894
9065
  const parsed = parseEcrUri(imageUri);
8895
9066
  if (!parsed) throw new LocalInvokeBuildError(`Image URI '${imageUri}' is not an ECR URI. ${getEmbedConfig().cliName} invoke v1 only authenticates against ECR for the deployed-image fallback path.`);
9067
+ const canonicalUri = parsed.canonicalUri;
8896
9068
  const callerRegion = options.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"];
8897
9069
  if (options.skipPull) {
8898
- logger.info(`Skipping ECR pull (--no-pull). Verifying ${imageUri} is in local cache...`);
8899
- await verifyImageInLocalCache(imageUri);
8900
- return imageUri;
9070
+ logger.info(`Skipping ECR pull (--no-pull). Verifying ${canonicalUri} is in local cache...`);
9071
+ await verifyImageInLocalCache(canonicalUri);
9072
+ return canonicalUri;
8901
9073
  }
8902
9074
  const callerIdentityKey = `${options.profile ?? "_noprofile"}|${callerRegion ?? "_unset"}`;
8903
9075
  let callerAccount = CALLER_IDENTITY_CACHE.get(callerIdentityKey);
@@ -8940,17 +9112,21 @@ async function pullEcrImage(imageUri, options) {
8940
9112
  ...assumed ? { credentials: assumed } : options.profile ? { profile: options.profile } : {}
8941
9113
  });
8942
9114
  try {
8943
- await ecrLogin(ecr, parsed.accountId, parsed.region);
9115
+ await ecrLogin(ecr, {
9116
+ accountId: parsed.accountId,
9117
+ region: parsed.region,
9118
+ registryHost: parsed.registryHost
9119
+ });
8944
9120
  } finally {
8945
9121
  ecr.destroy();
8946
9122
  }
8947
- logger.info(`Pulling ${imageUri}...`);
9123
+ logger.info(`Pulling ${canonicalUri}...`);
8948
9124
  try {
8949
- await runDockerForeground(["pull", imageUri]);
9125
+ await runDockerForeground(["pull", canonicalUri]);
8950
9126
  } catch (err) {
8951
- throw new LocalInvokeBuildError(`docker pull ${imageUri} failed: ${err.message}`);
9127
+ throw new LocalInvokeBuildError(`docker pull ${canonicalUri} failed: ${err.message}`);
8952
9128
  }
8953
- return imageUri;
9129
+ return canonicalUri;
8954
9130
  }
8955
9131
  /**
8956
9132
  * Assume the supplied role via the SDK default credential chain and
@@ -8991,16 +9167,29 @@ async function assumeRoleForEcr(roleArn, callerRegion, profile, logger) {
8991
9167
  * Authenticate the local docker daemon against the target ECR registry.
8992
9168
  * Self-contained in this module so the local-invoke path stays
8993
9169
  * independent of any full asset-publish path's larger surface area.
8994
- */
8995
- async function ecrLogin(client, accountId, region) {
8996
- getLogger().child("ecr-puller").debug(`ECR login (account=${accountId}, region=${region})`);
9170
+ *
9171
+ * The login endpoint is ALWAYS the host the PULL targets (issue #760):
9172
+ * docker's credential store is keyed on the hostname verbatim, so a login to
9173
+ * any other host leaves the pull with no credentials (`no basic auth
9174
+ * credentials`). `GetAuthorizationToken`'s `proxyEndpoint` is therefore NOT
9175
+ * used: it names the CALLER's default registry on the plain host, which is the
9176
+ * wrong host for a FIPS / dual-stack pull and the wrong ACCOUNT for a
9177
+ * cross-account pull made on the caller's own credentials (a repository policy
9178
+ * grant, no `--ecr-role-arn`). The token is principal-scoped, not host-scoped:
9179
+ * measured on real ECR, one token authenticates the plain, FIPS and dual-stack
9180
+ * hosts alike. The password reaches docker only over `--password-stdin`, never
9181
+ * argv.
9182
+ */
9183
+ async function ecrLogin(client, target) {
9184
+ const { accountId, region, registryHost } = target;
9185
+ getLogger().child("ecr-puller").debug(`ECR login (account=${accountId}, region=${region}, host=${registryHost})`);
8997
9186
  const authData = (await client.send(new GetAuthorizationTokenCommand({})).catch((err) => {
8998
9187
  throw new LocalInvokeBuildError(`ECR GetAuthorizationToken failed (account=${accountId}, region=${region}): ${describeAwsFailureForWarn(err, "ECR GetAuthorizationToken")}. Verify the credentials can call ecr:GetAuthorizationToken.`);
8999
9188
  })).authorizationData?.[0];
9000
9189
  if (!authData?.authorizationToken) throw new LocalInvokeBuildError("Failed to get ECR authorization token");
9001
9190
  const [username, password] = Buffer.from(authData.authorizationToken, "base64").toString().split(":");
9002
9191
  if (!username || password === void 0) throw new LocalInvokeBuildError("ECR authorization token has unexpected shape (missing username/password)");
9003
- const endpoint = authData.proxyEndpoint || `https://${accountId}.dkr.ecr.${region}.amazonaws.com`;
9192
+ const endpoint = `https://${registryHost}`;
9004
9193
  try {
9005
9194
  await runDockerStreaming([
9006
9195
  "login",
@@ -9298,7 +9487,7 @@ async function runDetached(opts) {
9298
9487
  args.push("-v", `${mount.hostPath}:${mount.containerPath}${ro}`);
9299
9488
  }
9300
9489
  const sensitiveKeys = opts.sensitiveEnvKeys && opts.sensitiveEnvKeys.size > 0 ? /* @__PURE__ */ new Set([...SENSITIVE_ENV_KEYS, ...opts.sensitiveEnvKeys]) : SENSITIVE_ENV_KEYS;
9301
- const passthroughEnv = appendEnvFlags(args, opts.env, sensitiveKeys);
9490
+ const { passthrough: passthroughEnv, collisions } = appendEnvFlags(args, opts.env, sensitiveKeys);
9302
9491
  if (opts.tmpfs) args.push("--tmpfs", `${opts.tmpfs.target}:rw,size=${opts.tmpfs.sizeMb}m`);
9303
9492
  if (opts.workingDir) args.push("--workdir", opts.workingDir);
9304
9493
  let entryPointTail = [];
@@ -9307,7 +9496,9 @@ async function runDetached(opts) {
9307
9496
  entryPointTail = opts.entryPoint.slice(1);
9308
9497
  }
9309
9498
  args.push(opts.image, ...entryPointTail, ...opts.cmd);
9310
- getLogger().child("docker").debug(`${getDockerCmd()} ${redactAwsCredentialsInArgs(args).join(" ")}`);
9499
+ const logger = getLogger().child("docker");
9500
+ logger.debug(`${getDockerCmd()} ${redactAwsCredentialsInArgs(args).join(" ")}`);
9501
+ if (collisions.length > 0) logger.warn(describeRefusedSensitiveEnvKeys(`Container for image ${displayUntrustedValue(opts.image)}`, collisions));
9311
9502
  const forwardedKeys = Object.keys(passthroughEnv);
9312
9503
  const refusal = finchSecretArgvRefusal(forwardedKeys.filter((k) => opts.sensitiveEnvKeys?.has(k)), `Container for image ${opts.image}`);
9313
9504
  if (refusal !== void 0) throw new DockerRunnerError(refusal);
@@ -9432,37 +9623,72 @@ const SENSITIVE_ENV_KEYS = /* @__PURE__ */ new Set([
9432
9623
  * shell history. Non-sensitive keys keep the inline `-e KEY=VALUE` form
9433
9624
  * (fine for non-secret config, and keeps `--debug` output readable).
9434
9625
  *
9435
- * Returns the `{ KEY: value }` map of routed-through keys; the caller MUST
9436
- * merge it into the spawned docker process's `env` (see
9626
+ * Returns `passthrough`, the `{ KEY: value }` map of routed-through keys; the
9627
+ * caller MUST merge it into the spawned docker process's `env` (see
9437
9628
  * {@link execEnvForSecrets}) so docker can resolve each passed-through
9438
9629
  * key. Values still appear in `docker inspect` Config.Env — that is
9439
9630
  * inherent to container env and matches production behavior.
9440
9631
  *
9632
+ * A sensitive key whose NAME is a variable the container client (or the
9633
+ * dynamic loader / a helper it execs) reads ({@link isDockerClientEnvKey}), or
9634
+ * that is MALFORMED ({@link isMalformedEnvKey}: empty, or containing `=` /
9635
+ * NUL), gets NO flag and no passthrough entry, and is returned in
9636
+ * `collisions` so the caller can warn by NAME (issue #772). Written into the
9637
+ * client's spawn env it would hijack the client (`DOCKER_HOST` redirects the
9638
+ * daemon, `PATH` / `LD_PRELOAD` choose what code runs); and a value-less
9639
+ * `-e KEY` the spawn env refuses to set would make docker resolve it against
9640
+ * its OWN environment, handing the container the HOST's value. A key
9641
+ * containing `=` is refused because the OS parses the environ entry's name as
9642
+ * everything before the first `=`, so `PATH=/tmp/evil:` would poison `PATH`
9643
+ * while slipping past the name denylist. A NON-sensitive key is container
9644
+ * env only (`-e KEY=VALUE` on the argv) and is unaffected.
9645
+ *
9441
9646
  * Unlike `--env-file`, this handles multi-line values (e.g. PEM secrets)
9442
9647
  * and needs no temp file on disk.
9443
9648
  */
9444
9649
  function appendEnvFlags(args, env, sensitiveKeys) {
9445
9650
  const passthrough = {};
9651
+ const collisions = [];
9446
9652
  for (const [k, v] of Object.entries(env)) if (sensitiveKeys.has(k)) {
9653
+ if (isMalformedEnvKey(k) || isDockerClientEnvKey(k)) {
9654
+ collisions.push(k);
9655
+ continue;
9656
+ }
9447
9657
  args.push("-e", k);
9448
- passthrough[k] = v;
9658
+ defineOwnKey(passthrough, k, v);
9449
9659
  } else args.push("-e", `${k}=${v}`);
9450
- return passthrough;
9660
+ return {
9661
+ passthrough,
9662
+ collisions
9663
+ };
9664
+ }
9665
+ /**
9666
+ * The warning for keys {@link appendEnvFlags} refused. Names each key
9667
+ * display-safe (a key is template-chosen) and never a value.
9668
+ */
9669
+ function describeRefusedSensitiveEnvKeys(subject, keys) {
9670
+ return `${subject}: sensitive env var(s) ${keys.map(displayUntrustedValue).join(", ")} share a name with a variable the container client reads (docker, or the CDK_DOCKER binary) or have a malformed name (empty, or containing '=' / NUL), and were NOT passed to the container at all (a colliding name would hijack that client; a malformed name cannot form a valid environment variable). Rename the variable if the container needs it.`;
9451
9671
  }
9452
9672
  /**
9453
9673
  * Build the `env` option for an `execFile` / `spawn` call that runs a
9454
9674
  * `docker run` whose args include passed-through sensitive keys (from
9455
- * {@link appendEnvFlags}). Returns `{ env: {...process.env, ...passthrough} }`
9456
- * so docker inherits the normal environment PLUS the sensitive values it
9457
- * must resolve, or `{}` when there is nothing to pass through (preserving
9458
- * the default inherited-environment behavior).
9675
+ * {@link appendEnvFlags}). Returns `{ env }`: a copy of `process.env` with the
9676
+ * accepted passthrough keys written onto it as own keys, so docker inherits
9677
+ * the normal environment PLUS the sensitive values it must resolve. Returns
9678
+ * `{}` when no key is left to pass through (preserving the default
9679
+ * inherited-environment behavior).
9680
+ *
9681
+ * The client's own variables stay authoritative: a passthrough key that names
9682
+ * one ({@link isDockerClientEnvKey}) or is malformed ({@link isMalformedEnvKey})
9683
+ * is never written (issue #772). {@link appendEnvFlags} already keeps such a
9684
+ * key out of `passthrough`; this is defence in depth for any other caller.
9459
9685
  */
9460
9686
  function execEnvForSecrets(passthrough) {
9461
- if (Object.keys(passthrough).length === 0) return {};
9462
- return { env: {
9463
- ...process.env,
9464
- ...passthrough
9465
- } };
9687
+ const keys = Object.keys(passthrough).filter((k) => !isMalformedEnvKey(k) && !isDockerClientEnvKey(k));
9688
+ if (keys.length === 0) return {};
9689
+ const env = { ...process.env };
9690
+ for (const k of keys) defineOwnKey(env, k, passthrough[k]);
9691
+ return { env };
9466
9692
  }
9467
9693
  /**
9468
9694
  * Returns a copy of `args` with any `-e <KEY>=<value>` pair whose KEY is
@@ -17287,6 +17513,40 @@ function availableApiIdentifiers(routes) {
17287
17513
  //#endregion
17288
17514
  //#region src/local/env-resolver.ts
17289
17515
  /**
17516
+ * Resolve a Lambda function's `Properties.Environment.Variables` for
17517
+ * `cdkl invoke`.
17518
+ *
17519
+ * Per the issue scope, v1 supports **literal values only**. Intrinsic
17520
+ * functions (`Ref` / `Fn::GetAtt` / `Fn::Sub` / etc.) in env vars are
17521
+ * unresolvable without state — they would substitute to whatever the
17522
+ * deployed value is, and we have no source for that here. Each unresolved
17523
+ * key is recorded in `unresolved` so the caller can warn the user; the
17524
+ * variable is **dropped** rather than silently substituted with garbage
17525
+ * (a bad env var that "exists" is harder to debug than one that's missing).
17526
+ *
17527
+ * `--env-vars <file>` overrides match SAM's shape (D5), with the
17528
+ * additional CDK ergonomics that a function-specific entry can be keyed
17529
+ * by either the CloudFormation logical ID or the CDK display path
17530
+ * (`Metadata['aws:cdk:path']`):
17531
+ *
17532
+ * {
17533
+ * "Parameters": { "GLOBAL_KEY": "value" },
17534
+ * "MyHandlerLogicalId": { "FUNCTION_KEY": "value" },
17535
+ * "MyStack/MyHandler": { "FUNCTION_KEY": "value" }
17536
+ * }
17537
+ *
17538
+ * Override merge order (lowest to highest priority):
17539
+ * 1. Template literal env vars
17540
+ * 2. `Parameters` (global, applied to every invoke)
17541
+ * 3. Function-specific entries (logical-ID or display-path keyed)
17542
+ * applied in JSON insertion order, so a later key wins on conflict.
17543
+ * This matches SAM's apply-in-order semantics; pick one form per
17544
+ * function to avoid surprises.
17545
+ *
17546
+ * The override file may also clear a variable by setting it to `null`
17547
+ * (matches SAM behavior).
17548
+ */
17549
+ /**
17290
17550
  * Resolve Lambda env vars for local invocation.
17291
17551
  *
17292
17552
  * @param logicalId The function's CloudFormation logical ID. Used
@@ -17310,7 +17570,7 @@ function availableApiIdentifiers(routes) {
17310
17570
  function resolveEnvVars$1(logicalId, displayPath, templateEnv, overrides) {
17311
17571
  const resolved = {};
17312
17572
  const unresolved = [];
17313
- if (templateEnv) for (const [key, value] of Object.entries(templateEnv)) if (isLiteralEnvValue(value)) resolved[key] = String(value);
17573
+ if (templateEnv) for (const [key, value] of Object.entries(templateEnv)) if (isLiteralEnvValue(value)) defineOwnKey(resolved, key, String(value));
17314
17574
  else unresolved.push(key);
17315
17575
  if (overrides) {
17316
17576
  applyEnvOverrideMap(resolved, overrides.Parameters);
@@ -17345,7 +17605,7 @@ function resolveEnvVars$1(logicalId, displayPath, templateEnv, overrides) {
17345
17605
  function applyEnvOverrideMap(acc, map) {
17346
17606
  if (!map) return;
17347
17607
  for (const [key, value] of Object.entries(map)) if (value === null) delete acc[key];
17348
- else if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") acc[key] = String(value);
17608
+ else if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") defineOwnKey(acc, key, String(value));
17349
17609
  }
17350
17610
  /**
17351
17611
  * A value is "literal" iff it's a string / number / boolean. CFn intrinsic
@@ -23471,7 +23731,8 @@ async function createNetworkAndSidecar(args) {
23471
23731
  if (credentials.sessionToken) sidecarEnv["AWS_SESSION_TOKEN"] = credentials.sessionToken;
23472
23732
  }
23473
23733
  if (cluster) sidecarEnv["CLUSTER"] = cluster;
23474
- const sidecarPassthroughEnv = appendEnvFlags(sidecarArgs, sidecarEnv, SENSITIVE_ENV_KEYS);
23734
+ const { passthrough: sidecarPassthroughEnv, collisions: sidecarCollisions } = appendEnvFlags(sidecarArgs, sidecarEnv, SENSITIVE_ENV_KEYS);
23735
+ if (sidecarCollisions.length > 0) logger.warn(describeRefusedSensitiveEnvKeys("Metadata sidecar", sidecarCollisions));
23475
23736
  sidecarArgs.push(METADATA_ENDPOINT_IMAGE);
23476
23737
  warnFinchArgvExposure(Object.keys(sidecarPassthroughEnv));
23477
23738
  logger.info(`Starting ECS local-container-endpoints sidecar at ${sidecarIp}...`);
@@ -23786,7 +24047,7 @@ async function runEcsTask(task, options, state) {
23786
24047
  const startOrder = topoSort(buildDependencyGraph(task.containers), task.containers);
23787
24048
  const finchRefusals = [];
23788
24049
  for (const c of task.containers) {
23789
- const refusal = finchSecretArgvRefusal([...c.secrets.map((s) => s.name), ...c.sensitiveEnvKeys], `Container '${c.name}'`);
24050
+ const refusal = finchSecretArgvRefusal([...c.secrets.map((s) => s.name), ...c.sensitiveEnvKeys].filter((n) => !isMalformedEnvKey(n) && !isDockerClientEnvKey(n)), `Container '${c.name}'`);
23790
24051
  if (refusal !== void 0) finchRefusals.push(refusal);
23791
24052
  }
23792
24053
  if (finchRefusals.length > 0) throw new EcsTaskRunnerError(finchRefusals.join("\n"));
@@ -23865,6 +24126,7 @@ async function runEcsTask(task, options, state) {
23865
24126
  args: built.args,
23866
24127
  sensitiveEnv: built.sensitiveEnv
23867
24128
  });
24129
+ if (built.collisions.length > 0) logger.warn(describeRefusedSensitiveEnvKeys(`Container ${displayUntrustedValue(container.name)}`, built.collisions));
23868
24130
  for (const ep of built.publishedEndpoints) state.publishedEndpoints.push(ep);
23869
24131
  }
23870
24132
  const startedByName = /* @__PURE__ */ new Map();
@@ -24308,8 +24570,8 @@ function buildDockerRunArgs(opts) {
24308
24570
  finalEnv["AWS_SHARED_CREDENTIALS_FILE"] = opts.profileCredentialsFile.containerPath;
24309
24571
  finalEnv["AWS_PROFILE"] = opts.profileCredentialsFile.profileName;
24310
24572
  }
24311
- Object.assign(finalEnv, container.environment);
24312
- for (const s of secrets) finalEnv[s.name] = s.value;
24573
+ for (const [k, v] of Object.entries(container.environment)) defineOwnKey(finalEnv, k, v);
24574
+ for (const s of secrets) defineOwnKey(finalEnv, s.name, s.value);
24313
24575
  const overrides = opts.envOverrides;
24314
24576
  if (overrides) {
24315
24577
  applyEnvOverrideMap(finalEnv, overrides["Parameters"]);
@@ -24318,7 +24580,7 @@ function buildDockerRunArgs(opts) {
24318
24580
  const sensitiveEnvKeys = new Set(SENSITIVE_ENV_KEYS);
24319
24581
  for (const s of secrets) sensitiveEnvKeys.add(s.name);
24320
24582
  for (const k of container.sensitiveEnvKeys) sensitiveEnvKeys.add(k);
24321
- const sensitiveEnv = appendEnvFlags(args, finalEnv, sensitiveEnvKeys);
24583
+ const { passthrough: sensitiveEnv, collisions } = appendEnvFlags(args, finalEnv, sensitiveEnvKeys);
24322
24584
  if (container.user) args.push("--user", container.user);
24323
24585
  if (container.privileged) args.push("--privileged");
24324
24586
  if (container.readonlyRootFilesystem) args.push("--read-only");
@@ -24341,6 +24603,7 @@ function buildDockerRunArgs(opts) {
24341
24603
  return {
24342
24604
  args,
24343
24605
  sensitiveEnv,
24606
+ collisions,
24344
24607
  publishedEndpoints
24345
24608
  };
24346
24609
  }
@@ -25514,7 +25777,7 @@ async function softReloadReplica(args) {
25514
25777
  const defaultDockerInspectWorkdirImpl = async (containerId) => {
25515
25778
  const { execFile } = await import("node:child_process");
25516
25779
  const { promisify } = await import("node:util");
25517
- const { getDockerCmd } = await import("./docker-cmd-B5yU__zC.js").then((n) => n.t);
25780
+ const { getDockerCmd } = await import("./docker-cmd-leyvDn1u.js").then((n) => n.t);
25518
25781
  const { stdout } = await promisify(execFile)(getDockerCmd(), [
25519
25782
  "inspect",
25520
25783
  "--format",
@@ -25533,7 +25796,7 @@ let dockerInspectWorkdirImpl = defaultDockerInspectWorkdirImpl;
25533
25796
  const defaultDockerCpImpl = async (src, dst) => {
25534
25797
  const { execFile } = await import("node:child_process");
25535
25798
  const { promisify } = await import("node:util");
25536
- const { getDockerCmd } = await import("./docker-cmd-B5yU__zC.js").then((n) => n.t);
25799
+ const { getDockerCmd } = await import("./docker-cmd-leyvDn1u.js").then((n) => n.t);
25537
25800
  await promisify(execFile)(getDockerCmd(), [
25538
25801
  "cp",
25539
25802
  src,
@@ -25548,7 +25811,7 @@ let dockerCpImpl = defaultDockerCpImpl;
25548
25811
  const defaultDockerRestartImpl = async (containerId) => {
25549
25812
  const { execFile } = await import("node:child_process");
25550
25813
  const { promisify } = await import("node:util");
25551
- const { getDockerCmd } = await import("./docker-cmd-B5yU__zC.js").then((n) => n.t);
25814
+ const { getDockerCmd } = await import("./docker-cmd-leyvDn1u.js").then((n) => n.t);
25552
25815
  await promisify(execFile)(getDockerCmd(), ["restart", containerId]);
25553
25816
  };
25554
25817
  let dockerRestartImpl = defaultDockerRestartImpl;
@@ -25588,7 +25851,7 @@ async function disconnectOldFromSharedNetwork(oldInstance) {
25588
25851
  const defaultDockerNetworkDisconnectImpl = async (networkName, containerId) => {
25589
25852
  const { execFile } = await import("node:child_process");
25590
25853
  const { promisify } = await import("node:util");
25591
- const { getDockerCmd } = await import("./docker-cmd-B5yU__zC.js").then((n) => n.t);
25854
+ const { getDockerCmd } = await import("./docker-cmd-leyvDn1u.js").then((n) => n.t);
25592
25855
  await promisify(execFile)(getDockerCmd(), [
25593
25856
  "network",
25594
25857
  "disconnect",
@@ -25765,7 +26028,7 @@ function pickEssentialContainerId(instance, service) {
25765
26028
  const defaultWaitForExitImpl = async (containerId) => {
25766
26029
  const { execFile } = await import("node:child_process");
25767
26030
  const { promisify } = await import("node:util");
25768
- const { getDockerCmd } = await import("./docker-cmd-B5yU__zC.js").then((n) => n.t);
26031
+ const { getDockerCmd } = await import("./docker-cmd-leyvDn1u.js").then((n) => n.t);
25769
26032
  const { stdout } = await promisify(execFile)(getDockerCmd(), ["wait", containerId], { maxBuffer: 1024 * 1024 });
25770
26033
  const code = parseInt(stdout.trim(), 10);
25771
26034
  return Number.isFinite(code) ? code : -1;
@@ -25785,7 +26048,7 @@ const EXIT_LOG_TAIL_LINES = 50;
25785
26048
  const defaultReadContainerLogsImpl = async (containerId) => {
25786
26049
  const { execFile } = await import("node:child_process");
25787
26050
  const { promisify } = await import("node:util");
25788
- const { getDockerCmd } = await import("./docker-cmd-B5yU__zC.js").then((n) => n.t);
26051
+ const { getDockerCmd } = await import("./docker-cmd-leyvDn1u.js").then((n) => n.t);
25789
26052
  const { stdout, stderr } = await promisify(execFile)(getDockerCmd(), [
25790
26053
  "logs",
25791
26054
  "--tail",
@@ -35092,7 +35355,7 @@ function resolveEnvVars(kind, values) {
35092
35355
  const flat = {};
35093
35356
  for (const row of value) {
35094
35357
  const { left, right } = row ?? {};
35095
- if (nonEmptyStr(left)) flat[left.trim()] = typeof right === "string" ? right : "";
35358
+ if (nonEmptyStr(left)) defineOwnKey(flat, left.trim(), typeof right === "string" ? right : "");
35096
35359
  }
35097
35360
  return Object.keys(flat).length > 0 ? { Parameters: flat } : void 0;
35098
35361
  }
@@ -40920,4 +41183,4 @@ function addStudioSpecificOptions(cmd) {
40920
41183
 
40921
41184
  //#endregion
40922
41185
  export { applyEdgeResponseResult as $, buildJwksUrlFromIssuer as $n, resolveCfnStackName as $r, buildCloudMapIndex as $t, startAgentCoreHttpServer as A, resolveProfileCredentials as Ai, classifySourceChange as An, ConnectionRegistry as Ar, addRunTaskSpecificOptions as At, idFromArn as B, buildStageMap as Bn, resolveRuntimeFileExtension as Br, resolveEcsAssumeRoleOption as Bt, addListSpecificOptions as C, resolveAgentCoreTarget as Ci, waitForAgentCorePing as Cn, tryParseStatus as Cr, parseLbPortOverrides as Ct, createLocalStartAgentCoreCommand as D, tryResolveImageFnJoin as Di, computeCodeImageTag as Dn, probeHostGatewaySupport as Dr, addStartServiceSpecificOptions as Dt, addStartAgentCoreSpecificOptions as E, substituteImagePlaceholders as Ei, buildAgentCoreCodeImage as En, HOST_GATEWAY_MIN_VERSION as Er, resolveAlbFrontDoor as Et, createLocalStartCloudFrontCommand as F, createWatchPredicates as Fn, buildDisconnectEvent as Fr, addImageOverrideOptions as Ft, classifyS3Error as G, filterRoutesByApiIdentifiers as Gn, substituteEnvVarsFromState as Gr, enforceImageOverrideOrphans as Gt, createDeployedKvsDataSource as H, resolveEnvVars$1 as Hn, EcsTaskResolutionError as Hr, runEcsServiceEmulator as Ht, normalizeKvsFileKeys as I, resolveApiTargetSubset as In, buildMessageEvent as Ir, buildEcsImageResolutionContext$1 as It, startCloudFrontServer as J, startApiServer as Jn, createLocalStateProvider as Jr, resolveImageOverrides as Jt, createS3OriginReader as K, groupRoutesByServer as Kn, substituteEnvVarsFromStateAsync as Kr, mergeForService as Kt, parseKvsFileOverrides as L, createAuthorizerCache as Ln, architectureToPlatform as Lr, ecsClusterOption as Lt, startAgentCoreWsBridge as M, isProxyEnvConfigured as Mi, createLocalInvokeCommand as Mn, handleConnectionsRequest as Mr, MAX_TASKS_SUBNET_RANGE_CAP as Mt, LocalStartCloudFrontError as N, addStartApiSpecificOptions as Nn, parseConnectionsPath as Nr, addCommonEcsServiceOptions as Nt, buildAgentCoreServeAuthCheck as O, LocalInvokeBuildError as Oi, renderCodeDockerfile as On, resolveHostGatewayExtraHosts as Or, createLocalStartServiceCommand as Ot, addStartCloudFrontSpecificOptions as P, createLocalStartApiCommand as Pn, buildConnectEvent as Pr, addEcsAssumeRoleOptions as Pt, applyEdgeRequestResult as Q, buildCognitoJwksUrl as Qn, resolveCfnRegion as Qr, listPinnedTargets as Qt, parseOriginOverrides as R, createFileWatcher as Rn, buildContainerImage as Rr, parseMaxTasks as Rt, StudioEventBus as S, pickAgentCoreCandidateStack as Si, waitForAgentCoreHttpReady as Sn, selectIntegrationResponse as Sr, createLocalStartAlbCommand as St, formatTargetListing as T, formatStateRemedy as Ti, SUPPORTED_CODE_RUNTIMES as Tn, HOST_DOCKER_INTERNAL_GATEWAY as Tr, isApplicationLoadBalancer as Tt, resolveDeployedKvsArnByName as U, availableApiIdentifiers as Un, substituteAgainstState as Ur, ImageOverrideError as Ut, resolveKvsModulesForDistribution as V, materializeLayerFromArn as Vn, resolveRuntimeImage as Vr, resolveSharedSidecarCredentials as Vt, resolveDeployedOriginBucket as W, filterRoutesByApiIdentifier as Wn, substituteAgainstStateAsync as Wr, buildImageOverrideTag as Wt, serveFromStaticOrigin as X, resolveServiceIntegrationParameters as Xn, rejectExplicitCfnStackWithMultipleStacks as Xr, describePinnedImageUri as Xt, resolveErrorResponseCandidates as Y, resolveSelectionExpression as Yn, isCfnFlagPresent as Yr, runImageOverrideBuilds as Yt, serveLambdaUrlOrigin as Z, defaultCredentialsLoader as Zn, resolveCfnFallbackRegion as Zr, isLocalCdkAssetImage as Zt, filterStudioTargetGroups as _, AGENTCORE_AGUI_PROTOCOL as _i, parseSseForJsonRpc as _n, applyAuthorizerOverlay as _r, createCloudFrontModule as _t, createLocalStudioCommand as a, countTargets as ai, attachContainerLogStreamer as an, computeRequestIdentityHash as ar, describeS3OriginDomain as at, renderStudioHtml as b, AGENTCORE_RUNTIME_TYPE as bi, AGENTCORE_SESSION_ID_HEADER as bn, evaluateResponseParameters as br, addAlbSpecificOptions as bt, startStudioProxy as c, discoverWebSocketApis as ci, bridgeAgentCoreWs as cn, invokeTokenAuthorizer as cr, pickFunctionUrlLogicalIdFromOrigin as ct, createStudioDispatcher as d, parseSelectionExpressionPath as di, A2A_PATH as dn, buildCorsConfigByApiId as dr, pickTargetFunctionLogicalId as dt, CfnLocalStateProvider as ei, CloudMapRegistry as en, createJwksCache as er, buildEdgeRequestEvent as et, filterStudioCustomResources as f, webSocketApiMatchesIdentifier as fi, a2aInvokeOnce as fn, buildCorsConfigFromCloudFrontChain as fr, resolveCloudFrontDistribution as ft, annotatePinnedEcsTargets as g, AGENTCORE_A2A_PROTOCOL as gi, mcpInvokeOnce as gn, translateLambdaResponse as gr, stripCloudFrontImport as gt, annotateEcsTaskPinnedTargets as h, resolveLambdaArnIntrinsic as hi, MCP_PROTOCOL_VERSION as hn, matchRoute as hr, runViewerResponse as ht, coerceStopRequest as i, resolveSingleTarget as ii, getContainerNetworkIp as in, buildMethodArn as ir, CLOUDFRONT_DISTRIBUTION_TYPE as it, attachAgentCoreWsBridge as j, buildProxyClientConfig as ji, addInvokeSpecificOptions as jn, buildMgmtEndpointEnvUrl as jr, createLocalRunTaskCommand as jt, selectServeInboundAuth as k, buildStsClientConfig as ki, toCmdArgv as kn, bufferToBody as kr, serviceStrategy as kt, relayServeRequest as l, discoverWebSocketApisOrThrow as li, invokeAgentCoreWs as ln, attachAuthorizers as lr, pickKvsLogicalIdFromArn as lt, annotateAlbPinnedBackingServices as m, pickRefLogicalId as mi, MCP_PATH as mn, matchPreflight as mr, runViewerRequest as mt, coerceRunRequest as n, resolveSsmParameters as ni, SOFT_RELOAD_COMPLETION_LOG_SUFFIX as nn, verifyJwtAuthorizer as nr, edgeHeadersToHttp as nt, resolveServeBaseUrl as o, listTargets as oi, addInvokeAgentCoreSpecificOptions as on, evaluateCachedLambdaPolicy as or, extractKvsAssociations as ot, isCustomResourceLambdaTarget as p, discoverRoutes as pi, MCP_CONTAINER_PORT as pn, isFunctionUrlOacFronted as pr, compileCloudFrontFunction as pt, matchBehavior as q, readMtlsMaterialsFromDisk as qn, LocalStateSourceError as qr, parseImageOverrideFlags as qt, coerceServeRequest as r, resolveWatchConfig as ri, setShadowReadyTimeoutMs as rn, verifyJwtViaDiscovery as rr, httpHeadersToEdge as rt, createStudioServeManager as s, availableWebSocketApiIdentifiers as si, createLocalInvokeAgentCoreCommand as sn, invokeRequestAuthorizer as sr, isCloudFrontDistribution as st, addStudioSpecificOptions as t, collectSsmParameterRefs as ti, DEFAULT_SHADOW_READY_TIMEOUT_MS as tn, verifyCognitoJwt as tr, buildEdgeResponseEvent as tt, reinvoke as u, filterWebSocketApisByIdentifiers as ui, A2A_CONTAINER_PORT as un, applyCorsResponseHeaders as ur, pickLambdaEdgeFunctionLogicalId as ut, startStudioServer as v, AGENTCORE_HTTP_PROTOCOL as vi, AGENTCORE_SIGV4_SERVICE as vn, buildHttpApiV2Event as vr, createLocalFileKvsDataSource as vt, createLocalListCommand as w, derivePseudoParametersFromRegion as wi, downloadAndExtractS3Bundle as wn, VtlEvaluationError as wr, resolveAlbTarget as wt, createStudioStore as x, AgentCoreResolutionError as xi, invokeAgentCore as xn, pickResponseTemplate as xr, albStrategy as xt, toStudioTargetGroups as y, AGENTCORE_MCP_PROTOCOL as yi, signAgentCoreInvocation as yn, buildRestV1Event as yr, createUnboundCloudFrontModule as yt, resolveCloudFrontTarget as z, attachStageContext as zn, resolveRuntimeCodeMountPath as zr, parseRestartPolicy as zt };
40923
- //# sourceMappingURL=local-studio-CV21zLBM.js.map
41186
+ //# sourceMappingURL=local-studio-U-gD2HSf.js.map