@go-to-k/cdkd 0.283.10 → 0.283.12
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-CrBdHIgB.js → asg-provider-BKR0cF_X.js} +2 -2
- package/dist/{asg-provider-CrBdHIgB.js.map → asg-provider-BKR0cF_X.js.map} +1 -1
- package/dist/cli.js +404 -208
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-Bvr31OQ0.js → deploy-engine-DHOpGcMS.js} +31 -7
- package/dist/deploy-engine-DHOpGcMS.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-Bvr31OQ0.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as coerceCfnBoolean, $t as
|
|
2
|
+
import { $ as coerceCfnBoolean, $t as resolveStateBucketWithDefault, A as cyan, An as MissingCdkCliError, At as AssetModeResolver, B as findSilentDropProperties, Bt as getDockerCmd, C as makeCanonicalizePropertiesFn, Ct as stringifyValue, D as renderStatefulReason, Dn as LocalMigrateError, Dt as loadPublishableAssetManifest, E as isStatefulRecreateTargetSync, En as LocalInvokeBuildError$1, Et as createAssetRedirectResolver, F as IAMRoleProvider, Fn as ResourceUpdateNotSupportedError, Ft as validateAssetBucketName, G as IntrinsicFunctionResolver, Gn as isRetryableTransientError, Gt as Synthesizer, H as slowCcOperationTimeoutMs, Hn as normalizeAwsError, Ht as runDockerStreaming, I as collectInlinePolicyNamesManagedBySiblings, In as StackHasActiveImportsError, It as validateContainerRepoName, J as refStateLookupFromResource, Jt as getLegacyStateBucketName, K as cfnRefValueFromPhysicalId, Kn as isThrottlingError, Kt as synthesisStatusMessage, L as clearOnUpdateRemoval, Ln as StackTerminationProtectionError, Lt as buildDenyExternalAccessPolicy, M as green, Mn as PartialFailureError, Mt as ensureAssetStorage, N as red, Nn as ProvisioningError, Nt as getBootstrapMarkerKey, O as formatResourceLine, On as LocalStartServiceError, Ot as rewriteTemplateAssetReferences, P as yellow, Pn as ResourceTimeoutError, Pt as parseBootstrapMarker, Q as assertRegionMatch, Qt as resolveSkipPrefix, R as ProviderRegistry, Rt as buildDockerImage, S as unsupportedFinalSnapshotError, Sn as CdkdError, St as AssetPublisher, T as MULTI_REGION_RECREATE_BLOCKED_TYPES, Tn as DeployCancelledError, Tt as buildAssetRedirectMap, U as disableInstanceApiTermination, Un as withErrorHandling, Ut as AssetManifestLoader, V as CloudControlProvider, Vt as runDockerForeground, W as isTerminationProtectionPropagationError, Wn as isMarkedNonRetryable, Wt as getDockerImageBySourceHash, X as normalizeAwsTagsToCfn, Xt as resolveAutoAssetStorage, Y as WAFv2WebACLProvider, Yt as resolveApp, Z as resolveExplicitPhysicalId, Zt as resolveCaptureObservedState, _ as buildFinalSnapshotIdentifier, _n as AwsClients, _t as TemplateParser, a as DeploymentEventsStore, an as CFN_TEMPLATE_URL_LIMIT, at as requireConfigObject, b as isFinalSnapshotError, bn as setAwsClients, bt as rebuildClientForBucketRegion, c as replayFailedOperations, cn as uploadCfnTemplate, ct as s3BucketDomainName, d as deleteSkipReason, dn as canonicalizeRegion, dt as s3BucketWebsiteUrl, en as resolveStateBucketWithDefaultAndSource, et as configBooleanRefusal, f as withResourceDeadline, fn as derivePartitionAndUrlSuffix, ft as applyRoleArnIfSet, g as PRE_DELETE_SNAPSHOT_TYPES, gn as resolveBucketRegion, gt as DagBuilder, h as ATOMIC_FINAL_SNAPSHOT_TYPES, ht as withRetry, i as DeploymentEventsReader, in as CFN_TEMPLATE_BODY_LIMIT, it as requireConfigArray, j as gray, jn as NestedStackChildDirectDestroyError, jt as BOOTSTRAP_MARKER_PREFIX, k as bold, kt as escapeRegExp, l as replayRollback, ln as expectedOwnerParam, lt as s3BucketDualStackDomainName, m as computeImplicitDeleteEdges, mn as processStackMessages, mt as describeTypeWithThrottleRetry, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as stateBucketExistenceConfirmed, nt as readConfigString, o as planFailedOps, on as MIGRATE_TMP_PREFIX, ot as requireConfigString, p as IMPLICIT_DELETE_DEPENDENCIES, pn as AssemblyReader, pt as DiffCalculator, q as getAccountInfo, qn as markNonRetryable, qt as getDefaultStateBucketName, r as DeployEngine, rn as warnDeprecatedNoPrefixCliFlag, rt as replayWarn, s as planRollback, sn as findLargeInlineResources, st as s3BucketArn, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as resolveUseCdkBootstrapAssets, tt as configStringRefusal, u as UNSPECIFIED_SKIP_REASON, un as PARTITION_TABLE, ut as s3BucketRegionalDomainName, v as ccRoutedFinalSnapshotError, vn as getAwsClients, vt as LockManager, w as extractDeploymentEventError, wt as WorkGraph, x as refusesFinalSnapshot, xt as shouldRetainResource, y as createPreDeleteFinalSnapshot, yn as resetAwsClients, yt as S3StateBackend, z as findActionableSilentDrops, zt as formatDockerLoginError } from "./deploy-engine-DHOpGcMS.js";
|
|
3
3
|
import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, h as withStackName, i as runStackBuffered, m as withSkipPrefix, n as getLogger, o as PATTERN_B_NAME_OPTIONS, s as PATTERN_B_NAME_PROPERTIES, u as generateResourceName } from "./logger-zRrlbaQt.js";
|
|
4
|
-
import { t as ASGProvider } from "./asg-provider-
|
|
4
|
+
import { t as ASGProvider } from "./asg-provider-BKR0cF_X.js";
|
|
5
5
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
6
6
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
7
7
|
import { CopyObjectCommand, CreateBucketCommand, DeleteBucketAnalyticsConfigurationCommand, DeleteBucketCommand, DeleteBucketCorsCommand, DeleteBucketEncryptionCommand, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketOwnershipControlsCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectsCommand, GetBucketAccelerateConfigurationCommand, GetBucketCorsCommand, GetBucketEncryptionCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketLoggingCommand, GetBucketNotificationConfigurationCommand, GetBucketOwnershipControlsCommand, GetBucketPolicyCommand, GetBucketReplicationCommand, GetBucketTaggingCommand, GetBucketVersioningCommand, GetBucketWebsiteCommand, GetObjectCommand, GetObjectLockConfigurationCommand, GetPublicAccessBlockCommand, HeadBucketCommand, ListBucketAnalyticsConfigurationsCommand, ListBucketIntelligentTieringConfigurationsCommand, ListBucketInventoryConfigurationsCommand, ListBucketMetricsConfigurationsCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchBucket, PutBucketAccelerateConfigurationCommand, PutBucketAnalyticsConfigurationCommand, PutBucketCorsCommand, PutBucketEncryptionCommand, PutBucketIntelligentTieringConfigurationCommand, PutBucketInventoryConfigurationCommand, PutBucketLifecycleConfigurationCommand, PutBucketLoggingCommand, PutBucketMetricsConfigurationCommand, PutBucketNotificationConfigurationCommand, PutBucketOwnershipControlsCommand, PutBucketPolicyCommand, PutBucketReplicationCommand, PutBucketTaggingCommand, PutBucketVersioningCommand, PutBucketWebsiteCommand, PutObjectCommand, PutObjectLockConfigurationCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
|
|
@@ -36881,7 +36881,7 @@ var Route53Provider = class {
|
|
|
36881
36881
|
physicalId: zoneId,
|
|
36882
36882
|
attributes: {
|
|
36883
36883
|
Id: zoneId,
|
|
36884
|
-
NameServers: nameServers
|
|
36884
|
+
NameServers: nameServers
|
|
36885
36885
|
}
|
|
36886
36886
|
};
|
|
36887
36887
|
} catch (error) {
|
|
@@ -36915,7 +36915,7 @@ var Route53Provider = class {
|
|
|
36915
36915
|
wasReplaced: false,
|
|
36916
36916
|
attributes: {
|
|
36917
36917
|
Id: physicalId,
|
|
36918
|
-
NameServers: nameServers
|
|
36918
|
+
NameServers: nameServers
|
|
36919
36919
|
}
|
|
36920
36920
|
};
|
|
36921
36921
|
} catch (error) {
|
|
@@ -37023,7 +37023,7 @@ var Route53Provider = class {
|
|
|
37023
37023
|
async getHostedZoneAttribute(physicalId, attributeName) {
|
|
37024
37024
|
switch (attributeName) {
|
|
37025
37025
|
case "Id": return physicalId;
|
|
37026
|
-
case "NameServers": return (
|
|
37026
|
+
case "NameServers": return (await this.getClient().send(new GetHostedZoneCommand({ Id: physicalId }))).DelegationSet?.NameServers ?? [];
|
|
37027
37027
|
default: return;
|
|
37028
37028
|
}
|
|
37029
37029
|
}
|
|
@@ -65786,6 +65786,363 @@ function createEventsPruneCommand() {
|
|
|
65786
65786
|
})).option("--older-than <duration>", "Delete runs older than this duration (e.g. 24h, 90m)").option("--all", "Delete every recorded run and the index (full purge)", false).action(withErrorHandling((stack, _options, command) => eventsPruneCommand(stack, command.optsWithGlobals())));
|
|
65787
65787
|
}
|
|
65788
65788
|
|
|
65789
|
+
//#endregion
|
|
65790
|
+
//#region src/utils/ecr-uri.ts
|
|
65791
|
+
/**
|
|
65792
|
+
* The ECR registry-host FORM TABLE — the ONE place the host forms AWS serves are
|
|
65793
|
+
* spelled (issue #1793).
|
|
65794
|
+
*
|
|
65795
|
+
* The forms used to be spelled TWICE, here and in `src/cli/commands/gc.ts`,
|
|
65796
|
+
* and the two already disagreed: gc carried the `-fips` and `on.aws` forms
|
|
65797
|
+
* while this module matched only the plain one, so a genuine FIPS or dual-stack
|
|
65798
|
+
* registry was not recognized here AT ALL and `cdkd local invoke` /
|
|
65799
|
+
* `run-task` classified it as a public image (anonymous pull, no
|
|
65800
|
+
* `docker login`).
|
|
65801
|
+
*
|
|
65802
|
+
* **What is shared is this TABLE, not the whole matcher** — read that precisely,
|
|
65803
|
+
* because the unification is deliberately PARTIAL. gc builds its pattern from
|
|
65804
|
+
* the table through {@link ecrRegistryHostPattern}; this module's own
|
|
65805
|
+
* {@link ECR_URI_HOST_REGEX} re-spells the alternation from the table's LABELS
|
|
65806
|
+
* column and cannot use that builder at all (its doc says why). So the shared
|
|
65807
|
+
* fact is which label runs AWS serves and which of them carry a FIXED suffix —
|
|
65808
|
+
* which is exactly what had drifted — while each matcher keeps its own
|
|
65809
|
+
* acceptance rule, deliberately: gc matches the suffix against the union over
|
|
65810
|
+
* every partition and {@link parseEcrRegistryHost} pairs it WITH the region (see
|
|
65811
|
+
* `gc.ts`'s `AWS_URL_SUFFIXES` doc for why the two strictnesses must not merge).
|
|
65812
|
+
*
|
|
65813
|
+
* A THIRD, looser spelling of "this is an ECR registry host" still lives outside
|
|
65814
|
+
* this table — `isCdkAssetImageUri`'s `host.includes('.dkr.ecr.')` in
|
|
65815
|
+
* `src/local/ecs-task-resolver.ts`, which recognizes only the plain form and is
|
|
65816
|
+
* case-SENSITIVE. It is tracked as issue #1846 rather than folded in here: it
|
|
65817
|
+
* must keep tolerating an UNRESOLVED host
|
|
65818
|
+
* (`${AWS::AccountId}.dkr.ecr.${AWS::Region}.${AWS::URLSuffix}`), so it cannot
|
|
65819
|
+
* simply become a call to either matcher.
|
|
65820
|
+
*
|
|
65821
|
+
* Every row is read off the AWS-published `ecr` endpoint list
|
|
65822
|
+
* (https://docs.aws.amazon.com/general/latest/gr/ecr.html) plus the docker
|
|
65823
|
+
* push / pull examples in the ECR user guide's "Making requests to Amazon ECR
|
|
65824
|
+
* registries", rather than inferred:
|
|
65825
|
+
*
|
|
65826
|
+
* - `<acct>.dkr.ecr.<region>.<urlSuffix>` — the IPv4-only registry endpoint,
|
|
65827
|
+
* every partition.
|
|
65828
|
+
* - `<acct>.dkr.ecr-fips.<region>.<urlSuffix>` — the FIPS IPv4 endpoint, served
|
|
65829
|
+
* in `us-east-1` / `us-east-2` / `us-west-1` / `us-west-2` /
|
|
65830
|
+
* `us-gov-east-1` / `us-gov-west-1` — i.e. only in partitions whose suffix is
|
|
65831
|
+
* `amazonaws.com`, which is why pairing it with the region's own partition
|
|
65832
|
+
* suffix is exactly right rather than merely convenient.
|
|
65833
|
+
* - `<acct>.dkr-ecr.<region>.on.aws` — the dual-stack (IPv4 + IPv6) endpoint.
|
|
65834
|
+
* - `<acct>.dkr-ecr-fips.<region>.on.aws` — the dual-stack FIPS endpoint, in the
|
|
65835
|
+
* same six regions as the IPv4 FIPS form. This one was missing from BOTH
|
|
65836
|
+
* copies of the grammar; unifying them is what surfaced it.
|
|
65837
|
+
*
|
|
65838
|
+
* The two dual-stack forms carry a FIXED suffix, so their check is that literal
|
|
65839
|
+
* rather than the region's partition suffix — the TIGHTEST available check for
|
|
65840
|
+
* them, not a relaxation: `on.aws` is an AWS-owned domain, so unlike a captured
|
|
65841
|
+
* suffix it cannot be substituted by a host someone else owns. They are
|
|
65842
|
+
* accepted for ANY region rather than only the partitions AWS documents them
|
|
65843
|
+
* in, deliberately: under-accepting is precisely the issue #1764 failure (a
|
|
65844
|
+
* cdkd table lagging AWS made a GENUINE registry classify as public), and the
|
|
65845
|
+
* suffix being AWS-owned means the widening cannot point a `docker login`
|
|
65846
|
+
* anywhere but AWS.
|
|
65847
|
+
*/
|
|
65848
|
+
const ECR_REGISTRY_HOST_FORMS = [
|
|
65849
|
+
{ labels: "dkr.ecr" },
|
|
65850
|
+
{ labels: "dkr.ecr-fips" },
|
|
65851
|
+
{
|
|
65852
|
+
labels: "dkr-ecr",
|
|
65853
|
+
fixedUrlSuffix: "on.aws"
|
|
65854
|
+
},
|
|
65855
|
+
{
|
|
65856
|
+
labels: "dkr-ecr-fips",
|
|
65857
|
+
fixedUrlSuffix: "on.aws"
|
|
65858
|
+
}
|
|
65859
|
+
];
|
|
65860
|
+
/**
|
|
65861
|
+
* {@link ECR_REGISTRY_HOST_FORMS} ordered LONGEST LABELS FIRST, so
|
|
65862
|
+
* `dkr.ecr-fips` is tried before its own `dkr.ecr` prefix instead of relying on
|
|
65863
|
+
* backtracking.
|
|
65864
|
+
*/
|
|
65865
|
+
const ECR_HOST_FORMS_LONGEST_FIRST = [...ECR_REGISTRY_HOST_FORMS].sort((a, b) => b.labels.length - a.labels.length);
|
|
65866
|
+
/**
|
|
65867
|
+
* {@link ECR_REGISTRY_HOST_FORMS} keyed by its (already lower-case) labels, for
|
|
65868
|
+
* resolving which suffix rule the matched form wants.
|
|
65869
|
+
*/
|
|
65870
|
+
const ECR_HOST_FORM_BY_LABELS = new Map(ECR_REGISTRY_HOST_FORMS.map((form) => [form.labels, form]));
|
|
65871
|
+
/**
|
|
65872
|
+
* The host half of an ECR image URI as a regex SOURCE string, built from
|
|
65873
|
+
* {@link ECR_REGISTRY_HOST_FORMS} (issue #1793).
|
|
65874
|
+
*
|
|
65875
|
+
* ONE consumer: `cdkd gc`, which passes loose non-capturing sub-patterns and
|
|
65876
|
+
* matches the suffix against the union over every partition.
|
|
65877
|
+
*
|
|
65878
|
+
* **This module does NOT call it**, and that is structural rather than an
|
|
65879
|
+
* oversight — {@link ECR_URI_HOST_REGEX} re-spells the alternation from the
|
|
65880
|
+
* table's LABELS column instead. The strict matcher needs the suffix CAPTURED,
|
|
65881
|
+
* at a FIXED group index, to pair it with the region; this builder inlines a
|
|
65882
|
+
* `fixedUrlSuffix` as an UNCAPTURED literal, so the capturing-group count would
|
|
65883
|
+
* differ per alternative and the group index of the suffix would depend on which
|
|
65884
|
+
* form matched. Making the fixed suffix capturing to even the count is not the
|
|
65885
|
+
* fix either: it would hand gc a group it does not want, ahead of the tag /
|
|
65886
|
+
* digest groups it reads BY POSITION. So the sharable unit is the table, not the
|
|
65887
|
+
* pattern — and the cross-matcher test in `tests/unit/cli/gc.test.ts` is driven
|
|
65888
|
+
* off the table, so a row added there must work on BOTH sides or the suite reds.
|
|
65889
|
+
*
|
|
65890
|
+
* Every group this function ADDS is non-capturing, and that is load-bearing
|
|
65891
|
+
* rather than stylistic, for the same by-position reason: `gc.ts` reads its ECR
|
|
65892
|
+
* tag / digest by GROUP INDEX, and a capturing group here would silently shift
|
|
65893
|
+
* them. The caller's own three sub-patterns are interpolated verbatim, so
|
|
65894
|
+
* whether THOSE capture is the caller's business.
|
|
65895
|
+
*/
|
|
65896
|
+
function ecrRegistryHostPattern(segments) {
|
|
65897
|
+
const forms = ECR_HOST_FORMS_LONGEST_FIRST.map((form) => {
|
|
65898
|
+
const suffix = form.fixedUrlSuffix === void 0 ? segments.partitionUrlSuffix : escapeRegExp(form.fixedUrlSuffix);
|
|
65899
|
+
return `${escapeRegExp(form.labels)}\\.${segments.region}\\.${suffix}`;
|
|
65900
|
+
});
|
|
65901
|
+
return `${segments.accountId}\\.(?:${forms.join("|")})`;
|
|
65902
|
+
}
|
|
65903
|
+
/**
|
|
65904
|
+
* Matching the HOST half of an ECR image URI:
|
|
65905
|
+
* `<acct>.<labels>.<region>.<urlSuffix>/`.
|
|
65906
|
+
*
|
|
65907
|
+
* The suffix is CAPTURED rather than spelled out (issue #1758): the previous
|
|
65908
|
+
* pattern hardcoded `amazonaws.com` with an optional `.cn` tail, so a `us-iso*`
|
|
65909
|
+
* registry (`c2s.ic.gov` / `sc2s.sgov.gov`) never matched and the caller
|
|
65910
|
+
* silently classified a real ECR image as a user-managed one — skipping the
|
|
65911
|
+
* `docker login` it needs.
|
|
65912
|
+
*
|
|
65913
|
+
* The LABELS are captured too, because which suffix is expected depends on
|
|
65914
|
+
* WHICH form matched (issue #1793) — the dual-stack forms carry a fixed
|
|
65915
|
+
* `on.aws` rather than the region's partition suffix.
|
|
65916
|
+
*
|
|
65917
|
+
* Built from {@link ECR_REGISTRY_HOST_FORMS}'s LABELS column rather than through
|
|
65918
|
+
* {@link ecrRegistryHostPattern}: the builder cannot serve this matcher's fixed
|
|
65919
|
+
* capture-group layout (see its doc). The table is what the two matchers share.
|
|
65920
|
+
*
|
|
65921
|
+
* The `i` flag makes the labels case-INSENSITIVE (issue #1792). DNS is
|
|
65922
|
+
* case-insensitive, so `<acct>.DKR.ECR.<region>.<suffix>` names the same host,
|
|
65923
|
+
* but the labels were spelled as case-sensitive literals — and unlike the
|
|
65924
|
+
* issue #1786 defect one layer over, the host then did not match the SHAPE at
|
|
65925
|
+
* all, so `parseEcrRegistryHost` returned `undefined` AND
|
|
65926
|
+
* {@link looksLikeEcrHostWithForeignSuffix} returned `false`, i.e. not even the
|
|
65927
|
+
* issue #1764 diagnostic fired.
|
|
65928
|
+
*
|
|
65929
|
+
* Widening the shape is the right fix rather than a refusal because DOCKER
|
|
65930
|
+
* accepts an upper-cased registry host, so cdkd is not made to `docker login`
|
|
65931
|
+
* for a URI docker will then reject. `distribution/reference`'s grammar spells
|
|
65932
|
+
* `domain-component` as `([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`
|
|
65933
|
+
* while the repository PATH is `alpha-numeric := [a-z0-9]+`, and
|
|
65934
|
+
* `ParseNormalizedNamed` raises `repository name must be lowercase` for the
|
|
65935
|
+
* REMOTE NAME only — `splitDockerDomain` performs no case folding on the
|
|
65936
|
+
* domain. This change never touches the repository path.
|
|
65937
|
+
*
|
|
65938
|
+
* What docker does NOT fold is its CREDENTIAL STORE, which is keyed on the
|
|
65939
|
+
* hostname verbatim (measured in issue #1801: a pull of an upper-cased host
|
|
65940
|
+
* sent no credentials at all, while the lower-cased spelling authenticated).
|
|
65941
|
+
* That is why the fix here is only the SHAPE match: the login / pull spelling
|
|
65942
|
+
* is reconciled one layer up by `ecr-puller.ts`'s `canonicalizeImageUriHost`,
|
|
65943
|
+
* which every docker-facing consumer already routes through. Issue #1817
|
|
65944
|
+
* tracks moving that fold in here.
|
|
65945
|
+
*
|
|
65946
|
+
* **Every verdict issues #1792 / #1793 changed, in full** — `p` is
|
|
65947
|
+
* {@link parseEcrRegistryHost}, `d` is
|
|
65948
|
+
* {@link looksLikeEcrHostWithForeignSuffix} (the issue #1764 diagnostic), `S`
|
|
65949
|
+
* is the region's own partition suffix and `F` a foreign one. The table is
|
|
65950
|
+
* spelled out because the DIAGNOSTIC-ONLY rows are the easy ones to omit: `p`
|
|
65951
|
+
* is unchanged there, so a summary that tracks only the parse verdict reads as
|
|
65952
|
+
* "no change" while a warning that never used to fire now does.
|
|
65953
|
+
*
|
|
65954
|
+
* ```text
|
|
65955
|
+
* host before after what changed
|
|
65956
|
+
* <a>.dkr.ecr.<r>.S p=ok d=f p=ok d=f nothing
|
|
65957
|
+
* <a>.dkr.ecr.<r>.F p=und d=TRUE p=und d=TRUE nothing
|
|
65958
|
+
* <a>.dkr.ecr.<r>.on.aws p=und d=TRUE p=und d=TRUE nothing (*)
|
|
65959
|
+
* <a>.DKR.ECR.<r>.S p=und d=f p=ok d=f #1792 PARSE
|
|
65960
|
+
* <a>.DKR.ECR.<r>.F p=und d=f p=und d=TRUE #1792 diag only
|
|
65961
|
+
* <a>.dkr.ecr-fips.<r>.S p=und d=f p=ok d=f #1793 PARSE
|
|
65962
|
+
* <a>.dkr.ecr-fips.<r>.F p=und d=f p=und d=TRUE #1793 diag only
|
|
65963
|
+
* <a>.dkr-ecr.<r>.on.aws p=und d=f p=ok d=f #1793 PARSE
|
|
65964
|
+
* <a>.dkr-ecr-fips.<r>.on.aws p=und d=f p=ok d=f #1793 PARSE
|
|
65965
|
+
* <a>.dkr-ecr.<r>.S p=und d=f p=und d=TRUE #1793 diag only
|
|
65966
|
+
* <a>.dkr-ecr-fips.<r>.S p=und d=f p=und d=TRUE #1793 diag only
|
|
65967
|
+
* <a>.dkr.ecr-fips.<r>.on.aws p=und d=f p=und d=TRUE #1793 diag only
|
|
65968
|
+
* ```
|
|
65969
|
+
*
|
|
65970
|
+
* (*) `dkr.ecr` + `on.aws` is the one mispairing that ALREADY reported `true`
|
|
65971
|
+
* before the change: the pre-#1793 pattern matched the plain labels against a
|
|
65972
|
+
* wildcard `([^/]+)` suffix, so `on.aws` was simply a suffix that is not
|
|
65973
|
+
* `amazonaws.com`. Every other row that flips `d` was previously refused at the
|
|
65974
|
+
* SHAPE, which is why both verdicts were quiet.
|
|
65975
|
+
*
|
|
65976
|
+
* The five `d`-only flips are an improvement in kind — a host AWS does not serve
|
|
65977
|
+
* now says so instead of passing silently as a public image — but the caller's
|
|
65978
|
+
* MESSAGE was written for the #1764 partition-gap case and now over-claims for a
|
|
65979
|
+
* form/suffix mispairing (`amazonaws.com` IS `us-east-1`'s partition suffix).
|
|
65980
|
+
* `src/local/ecs-task-resolver.ts` owns that wording; recorded on issue #1846.
|
|
65981
|
+
*/
|
|
65982
|
+
const ECR_URI_HOST_REGEX = new RegExp(`^(\\d{12})\\.(${ECR_HOST_FORMS_LONGEST_FIRST.map((form) => escapeRegExp(form.labels)).join("|")})\\.([^.]+)\\.([^/]+)/`, "i");
|
|
65983
|
+
/**
|
|
65984
|
+
* A region segment cdkd is willing to treat as a region id.
|
|
65985
|
+
*
|
|
65986
|
+
* Case is not the only way the `startsWith` partition classification can be
|
|
65987
|
+
* side-stepped: any leading junk defeats it the same way, and the captured
|
|
65988
|
+
* segment does not stay inert — it becomes an `ECRClient({region})` and is
|
|
65989
|
+
* interpolated into the fallback `docker login` endpoint. Measured before this
|
|
65990
|
+
* guard: `…dkr.ecr. us-iso-east-1.amazonaws.com/…` (leading space) and a
|
|
65991
|
+
* combining-mark form both parsed, yielding regions `" us-iso-east-1"` and
|
|
65992
|
+
* `"us-i̇so-east-1"`. Neither leaks credentials — an ACCEPTED suffix is always
|
|
65993
|
+
* an AWS-owned domain — but a region id is ASCII alphanumeric plus `-`, so
|
|
65994
|
+
* anything else is a malformed host and refusing it keeps the classification
|
|
65995
|
+
* honest.
|
|
65996
|
+
*
|
|
65997
|
+
* TESTED AGAINST THE RAW SEGMENT, BEFORE `toLowerCase()`, and that order is the
|
|
65998
|
+
* whole point: `String.prototype.toLowerCase` performs full Unicode case
|
|
65999
|
+
* folding, so the Kelvin sign U+212A folds to a plain ASCII `k`. Checking the
|
|
66000
|
+
* FOLDED segment therefore accepts `us-eKst-1` and yields the region
|
|
66001
|
+
* `us-ekst-1` — a region the host does not name — which is precisely the
|
|
66002
|
+
* class of substitution this guard exists to refuse. Matching `[A-Za-z0-9-]`
|
|
66003
|
+
* on the raw capture keeps case-insensitivity (the point of issue #1786) while
|
|
66004
|
+
* admitting only characters that fold to themselves.
|
|
66005
|
+
*
|
|
66006
|
+
* ONE DELIBERATE SIDE EFFECT, called out because it is a withdrawal rather than
|
|
66007
|
+
* an addition: a malformed-region host is now classified as an ordinary public
|
|
66008
|
+
* image with NO cdkd-side signal at all — neither the ECR verdict nor the #1764
|
|
66009
|
+
* foreign-suffix diagnostic. Both arms change, not just the foreign one:
|
|
66010
|
+
* `<acct>.dkr.ecr.us_east_1.example.com/…` used to log the partition-gap
|
|
66011
|
+
* warning and now logs nothing, and `<acct>.dkr.ecr.us_east_1.amazonaws.com/…`
|
|
66012
|
+
* used to be ACCEPTED as ECR and is now refused. That is the correct verdict
|
|
66013
|
+
* rather than a regression — the diagnostic's whole subject is "this suffix does
|
|
66014
|
+
* not belong to its region's PARTITION", and a segment that is not a region id
|
|
66015
|
+
* has no partition to belong to, so reporting it would send the reader hunting
|
|
66016
|
+
* for a table entry that could never exist — but it does mean a typo'd region
|
|
66017
|
+
* gets no cdkd-side hint, only docker's own pull failure. Both arms are pinned
|
|
66018
|
+
* by tests so a future widening of the guard cannot flip them silently.
|
|
66019
|
+
*/
|
|
66020
|
+
const CANONICAL_REGION_SEGMENT = /^[A-Za-z0-9][A-Za-z0-9-]*$/;
|
|
66021
|
+
/**
|
|
66022
|
+
* A URL suffix cdkd is willing to compare against the partition table.
|
|
66023
|
+
*
|
|
66024
|
+
* The SAME raw-capture rule as the region, and for the same reason one layer
|
|
66025
|
+
* over: the suffix is folded before the comparison, so a code point folding
|
|
66026
|
+
* into ASCII can impersonate a partition's suffix. This became reachable the
|
|
66027
|
+
* moment issue #1764 landed `aws-isoe`'s `cloud.adc-e.uk` — that suffix
|
|
66028
|
+
* contains a `k`, so `…dkr.ecr.eu-isoe-west-1.cloud.adc-e.uK/…` (Kelvin
|
|
66029
|
+
* sign) folded onto it and was ACCEPTED. Not a credential leak, since UTS-46
|
|
66030
|
+
* maps U+212A to `k` too and the host resolves to the real AWS domain, but it
|
|
66031
|
+
* falsifies the invariant the region guard's comment above states, so both
|
|
66032
|
+
* captures are held to the same rule rather than only the one that was
|
|
66033
|
+
* measured first.
|
|
66034
|
+
*
|
|
66035
|
+
* Dots are admitted because a suffix is a dotted DNS name; every other
|
|
66036
|
+
* character is refused, so the accepted set stays exactly the partition
|
|
66037
|
+
* table's own literals.
|
|
66038
|
+
*/
|
|
66039
|
+
const CANONICAL_URL_SUFFIX = /^[A-Za-z0-9][A-Za-z0-9.-]*$/;
|
|
66040
|
+
/**
|
|
66041
|
+
* The ONE case-normalization boundary of this module (issue #1786).
|
|
66042
|
+
*
|
|
66043
|
+
* DNS is case-INSENSITIVE, so `US-ISO-EAST-1.amazonaws.com` and
|
|
66044
|
+
* `us-iso-east-1.amazonaws.com` name the same host — but both halves of the
|
|
66045
|
+
* #1758 suffix check were case-SENSITIVE, in OPPOSITE directions:
|
|
66046
|
+
*
|
|
66047
|
+
* - an upper-cased REGION fails every `startsWith` prefix test in
|
|
66048
|
+
* `derivePartitionAndUrlSuffix`, so it falls through to the commercial
|
|
66049
|
+
* partition whose suffix is `amazonaws.com` — which is exactly what an ISO
|
|
66050
|
+
* look-alike host carries, so the strict check ACCEPTED it (the bypass);
|
|
66051
|
+
* - an upper-cased region or SUFFIX on a GENUINE host stopped matching its own
|
|
66052
|
+
* partition's suffix, so a real ECR registry was REJECTED and the caller
|
|
66053
|
+
* silently classified it as a user-managed image — the #1758 regression, one
|
|
66054
|
+
* layer up.
|
|
66055
|
+
*
|
|
66056
|
+
* The normalization lives HERE rather than inside `derivePartitionAndUrlSuffix`
|
|
66057
|
+
* because this is the boundary where an untrusted, DNS-shaped string enters
|
|
66058
|
+
* cdkd, and keeping it in ONE function means both exported entry points below —
|
|
66059
|
+
* and the suffix COMPARISON itself — cannot drift apart.
|
|
66060
|
+
*
|
|
66061
|
+
* This does NOT duplicate `canonicalizeRegion`, which issue #1795 added inside
|
|
66062
|
+
* `derivePartitionAndUrlSuffix` so the mapping folds its own input. The two
|
|
66063
|
+
* answer different questions and both are needed: that one makes the partition
|
|
66064
|
+
* LOOKUP case-insensitive for every caller, while these guards decide whether
|
|
66065
|
+
* the captured segments are a region id and a URL suffix AT ALL. Folding alone
|
|
66066
|
+
* cannot do that — folding is precisely what turns `us-eKst-1` into the
|
|
66067
|
+
* plausible `us-ekst-1` (see the guards' own comments), so a fold-only helper
|
|
66068
|
+
* makes the substitution easier to reach, not harder. Double-folding is a
|
|
66069
|
+
* no-op, which is why the redundant `toLowerCase()` calls below are left in
|
|
66070
|
+
* rather than removed: they keep this function the single answer for the
|
|
66071
|
+
* comparison even if the helper's behaviour changes again.
|
|
66072
|
+
*
|
|
66073
|
+
* The derived suffix is lower-cased too. Every entry in the partition table is
|
|
66074
|
+
* a lower-case literal today, so that is a no-op; it is there so this function
|
|
66075
|
+
* stays the single answer even if that table ever gains a mixed-case entry.
|
|
66076
|
+
*/
|
|
66077
|
+
function matchEcrRegistryHost(imageUri) {
|
|
66078
|
+
const m = ECR_URI_HOST_REGEX.exec(imageUri);
|
|
66079
|
+
if (!m) return void 0;
|
|
66080
|
+
if (!CANONICAL_REGION_SEGMENT.test(m[3])) return void 0;
|
|
66081
|
+
if (!CANONICAL_URL_SUFFIX.test(m[4])) return void 0;
|
|
66082
|
+
const form = ECR_HOST_FORM_BY_LABELS.get(m[2].toLowerCase());
|
|
66083
|
+
if (!form) return void 0;
|
|
66084
|
+
const region = m[3].toLowerCase();
|
|
66085
|
+
return {
|
|
66086
|
+
accountId: m[1],
|
|
66087
|
+
region,
|
|
66088
|
+
suffix: m[4].toLowerCase(),
|
|
66089
|
+
expectedSuffix: (form.fixedUrlSuffix ?? derivePartitionAndUrlSuffix(region).urlSuffix).toLowerCase()
|
|
66090
|
+
};
|
|
66091
|
+
}
|
|
66092
|
+
/**
|
|
66093
|
+
* The account + region of an ECR image URI, or `undefined` when the URI is not
|
|
66094
|
+
* an ECR registry host — either because it does not have one of the shapes
|
|
66095
|
+
* {@link ECR_REGISTRY_HOST_FORMS} lists, or because its suffix is not the one
|
|
66096
|
+
* the matched form carries for the region it names.
|
|
66097
|
+
*
|
|
66098
|
+
* Lives in `src/utils/` rather than beside its first caller because it has TWO
|
|
66099
|
+
* consumers in different layers — `src/local/ecr-puller.ts` (which needs the
|
|
66100
|
+
* `:tag` tail too) and `src/local/ecs-task-resolver.ts` (which classifies an
|
|
66101
|
+
* image that may carry a digest or no tag at all). Before issue #1758 they each
|
|
66102
|
+
* carried their own copy of the hardcoded commercial pattern, and fixing only
|
|
66103
|
+
* the pull path left `cdkd local run-task` broken outside the commercial
|
|
66104
|
+
* partition; ONE definition is what stops them drifting apart again.
|
|
66105
|
+
*
|
|
66106
|
+
* The module is deliberately free of AWS SDK imports, which is what lets
|
|
66107
|
+
* `ecs-task-resolver.ts` consume it without breaking the invariant that module
|
|
66108
|
+
* documents about itself (it resolves secrets through a separate module for
|
|
66109
|
+
* exactly that reason).
|
|
66110
|
+
*
|
|
66111
|
+
* The returned `region` is LOWER-CASED (issue #1786) — it is consumed as an SDK
|
|
66112
|
+
* client region by `ecr-puller.ts` and as the `region` of a `kind: 'ecr'` image
|
|
66113
|
+
* by `ecs-task-resolver.ts`, and the canonical spelling of an AWS region id is
|
|
66114
|
+
* lower case.
|
|
66115
|
+
*/
|
|
66116
|
+
function parseEcrRegistryHost(imageUri) {
|
|
66117
|
+
const m = matchEcrRegistryHost(imageUri);
|
|
66118
|
+
if (!m) return void 0;
|
|
66119
|
+
if (m.suffix !== m.expectedSuffix) return void 0;
|
|
66120
|
+
return {
|
|
66121
|
+
accountId: m.accountId,
|
|
66122
|
+
region: m.region
|
|
66123
|
+
};
|
|
66124
|
+
}
|
|
66125
|
+
/**
|
|
66126
|
+
* True when the URI has one of the ECR HOST SHAPES but carries a suffix that
|
|
66127
|
+
* form does not serve for the region it names — i.e. exactly the case
|
|
66128
|
+
* {@link parseEcrRegistryHost} rejects for a reason the caller may want to
|
|
66129
|
+
* report.
|
|
66130
|
+
*
|
|
66131
|
+
* Since issue #1793 that covers a form / suffix MISPAIRING too, not only a
|
|
66132
|
+
* foreign suffix: `<acct>.dkr-ecr.<region>.amazonaws.com` and
|
|
66133
|
+
* `<acct>.dkr.ecr.<region>.on.aws` each spell one form's labels with the
|
|
66134
|
+
* other's suffix, and AWS serves neither.
|
|
66135
|
+
*
|
|
66136
|
+
* The two rejections are worth telling apart at a call site that degrades
|
|
66137
|
+
* silently: a genuinely public image and a registry in a partition
|
|
66138
|
+
* `derivePartitionAndUrlSuffix` does not know yet (issue #1764) both come back
|
|
66139
|
+
* `undefined`, and only the second is a cdkd gap rather than a user choice.
|
|
66140
|
+
*/
|
|
66141
|
+
function looksLikeEcrHostWithForeignSuffix(imageUri) {
|
|
66142
|
+
const m = matchEcrRegistryHost(imageUri);
|
|
66143
|
+
return m !== void 0 && m.suffix !== m.expectedSuffix;
|
|
66144
|
+
}
|
|
66145
|
+
|
|
65789
66146
|
//#endregion
|
|
65790
66147
|
//#region src/cli/commands/gc.ts
|
|
65791
66148
|
/**
|
|
@@ -65845,10 +66202,6 @@ function parseOlderThan(value) {
|
|
|
65845
66202
|
const multiplier = match[2] === "d" ? 1440 * 60 * 1e3 : 3600 * 1e3;
|
|
65846
66203
|
return Math.round(num * multiplier);
|
|
65847
66204
|
}
|
|
65848
|
-
/** Escape a literal string for embedding in a RegExp. */
|
|
65849
|
-
function escapeRegExp(value) {
|
|
65850
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
65851
|
-
}
|
|
65852
66205
|
/**
|
|
65853
66206
|
* Characters that terminate an S3 key extracted from a URL-shaped string.
|
|
65854
66207
|
* Whitespace / quotes never appear in cdkd's content-addressed asset keys
|
|
@@ -65886,30 +66239,50 @@ const GC_EXTRA_URL_SUFFIXES = [];
|
|
|
65886
66239
|
*/
|
|
65887
66240
|
const URL_SUFFIX_ALTERNATION = `(?:${[...[.../* @__PURE__ */ new Set([...PARTITION_TABLE.map((row) => row.urlSuffix), ...GC_EXTRA_URL_SUFFIXES])]].sort((a, b) => b.length - a.length).map(escapeRegExp).join("|")})`;
|
|
65888
66241
|
/**
|
|
65889
|
-
* The HOST half of an ECR registry,
|
|
65890
|
-
*
|
|
65891
|
-
*
|
|
65892
|
-
*
|
|
65893
|
-
*
|
|
65894
|
-
*
|
|
65895
|
-
*
|
|
65896
|
-
*
|
|
65897
|
-
*
|
|
65898
|
-
*
|
|
65899
|
-
*
|
|
65900
|
-
*
|
|
66242
|
+
* The HOST half of an ECR registry, built from the ONE authoritative FORM TABLE
|
|
66243
|
+
* in `src/utils/ecr-uri.ts` (issue #1793) instead of spelled a second time here.
|
|
66244
|
+
*
|
|
66245
|
+
* That module owns which segment spellings AWS serves — the plain
|
|
66246
|
+
* `<acct>.dkr.ecr.<region>.<urlSuffix>`, its FIPS sibling
|
|
66247
|
+
* `<acct>.dkr.ecr-fips.<region>.<urlSuffix>`, and the two dual-stack forms
|
|
66248
|
+
* `<acct>.dkr-ecr[-fips].<region>.on.aws` — while each matcher keeps its own
|
|
66249
|
+
* suffix-ACCEPTANCE rule on top: gc passes the union over every partition
|
|
66250
|
+
* ({@link URL_SUFFIX_ALTERNATION}), whereas `parseEcrRegistryHost` pairs the
|
|
66251
|
+
* captured suffix WITH the region. See the {@link AWS_URL_SUFFIXES} doc for why
|
|
66252
|
+
* the two strictnesses are deliberate and must not be merged.
|
|
66253
|
+
*
|
|
66254
|
+
* gc is the ONLY consumer of {@link ecrRegistryHostPattern}: the strict matcher
|
|
66255
|
+
* there needs the suffix captured at a fixed group index and so re-spells the
|
|
66256
|
+
* alternation off the same table's labels column. What the two share is the
|
|
66257
|
+
* TABLE, not the pattern — enough to stop the FORMS drifting, which is what had
|
|
66258
|
+
* gone wrong, and the cross-matcher test in `tests/unit/cli/gc.test.ts` is driven
|
|
66259
|
+
* off the table so a row added there must work on both sides.
|
|
66260
|
+
*
|
|
66261
|
+
* Sharing the table immediately brought a form gc was MISSING:
|
|
66262
|
+
* `<acct>.dkr-ecr-fips.<region>.on.aws`, the dual-stack FIPS endpoint. A form
|
|
66263
|
+
* missing here reads a live image as unreferenced and DELETES it, so this is the
|
|
66264
|
+
* irreversible direction — which is the whole argument for one definition.
|
|
66265
|
+
*
|
|
66266
|
+
* `on.aws` still does not reach the S3 matchers, and now cannot: it is an
|
|
66267
|
+
* ECR-only endpoint, so folding it into the shared suffix set would also make
|
|
66268
|
+
* `https://<assetBucket>.s3.<region>.on.aws/<key>` a recognized S3 reference —
|
|
66269
|
+
* and in the form table a fixed suffix travels WITH its own form rather than
|
|
66270
|
+
* sitting in a shared set, so that separation is mechanical instead of
|
|
66271
|
+
* remembered.
|
|
65901
66272
|
*
|
|
65902
66273
|
* Account and region are matched loosely on purpose (see the extractor doc):
|
|
65903
66274
|
* collecting a reference from another account's or region's URI can only KEEP
|
|
65904
|
-
* more, never delete more — which is also why the
|
|
65905
|
-
* carrying even though cdkd's own publisher writes the plain one.
|
|
65906
|
-
* tracks deriving these host forms from ONE place shared with
|
|
65907
|
-
* `src/utils/ecr-uri.ts` instead of two hand-kept copies — that module matches
|
|
65908
|
-
* only the plain `dkr.ecr` form today, so the two already disagree.
|
|
66275
|
+
* more, never delete more — which is also why the extra forms are worth
|
|
66276
|
+
* carrying even though cdkd's own publisher writes the plain one.
|
|
65909
66277
|
*
|
|
65910
|
-
* Every group is non-capturing, for the {@link URL_SUFFIX_ALTERNATION} reason
|
|
66278
|
+
* Every group is non-capturing, for the {@link URL_SUFFIX_ALTERNATION} reason;
|
|
66279
|
+
* {@link ecrRegistryHostPattern} guarantees it for the groups IT adds.
|
|
65911
66280
|
*/
|
|
65912
|
-
const ECR_REGISTRY_HOST =
|
|
66281
|
+
const ECR_REGISTRY_HOST = ecrRegistryHostPattern({
|
|
66282
|
+
accountId: "\\d{12}",
|
|
66283
|
+
region: "[a-z0-9-]+",
|
|
66284
|
+
partitionUrlSuffix: URL_SUFFIX_ALTERNATION
|
|
66285
|
+
});
|
|
65913
66286
|
/**
|
|
65914
66287
|
* cdkd's publishers write content-addressed keys (`<sha256>.<ext>`). A
|
|
65915
66288
|
* second, name-independent pass collects every such token from every
|
|
@@ -65967,7 +66340,7 @@ function buildReferenceExtractors(marker) {
|
|
|
65967
66340
|
const s3UriRe = new RegExp(`s3://${bucket}/(${KEY_TERMINATORS}+)`, "g");
|
|
65968
66341
|
const virtualHostedRe = new RegExp(`https://${bucket}\\.s3[^/\\s]*\\.${URL_SUFFIX_ALTERNATION}/(${KEY_TERMINATORS}+)`, "g");
|
|
65969
66342
|
const pathStyleRe = new RegExp(`https://s3[^/\\s]*\\.${URL_SUFFIX_ALTERNATION}/${bucket}/(${KEY_TERMINATORS}+)`, "g");
|
|
65970
|
-
const ecrRe = new RegExp(`${ECR_REGISTRY_HOST}/${repo}(?::([A-Za-z0-9_][A-Za-z0-9._-]{0,127}))?(?:@(sha256:[0-9a-f]{64}))?`, "
|
|
66343
|
+
const ecrRe = new RegExp(`${ECR_REGISTRY_HOST}/${repo}(?::([A-Za-z0-9_][A-Za-z0-9._-]{0,127}))?(?:@(sha256:[0-9a-f]{64}))?`, "gi");
|
|
65971
66344
|
const extractOnce = (value, refs) => {
|
|
65972
66345
|
for (const re of [
|
|
65973
66346
|
s3UriRe,
|
|
@@ -65976,7 +66349,7 @@ function buildReferenceExtractors(marker) {
|
|
|
65976
66349
|
]) for (const match of value.matchAll(re)) if (match[1]) refs.s3Keys.add(match[1]);
|
|
65977
66350
|
for (const match of value.matchAll(ecrRe)) {
|
|
65978
66351
|
if (match[1]) refs.imageTags.add(match[1]);
|
|
65979
|
-
if (match[2]) refs.imageDigests.add(match[2]);
|
|
66352
|
+
if (match[2]) refs.imageDigests.add(match[2].toLowerCase());
|
|
65980
66353
|
}
|
|
65981
66354
|
for (const match of value.matchAll(CONTENT_HASH_KEY_RE)) refs.s3Keys.add(match[0]);
|
|
65982
66355
|
};
|
|
@@ -69183,183 +69556,6 @@ function notFoundError$1(target, stack, resources) {
|
|
|
69183
69556
|
return new LocalInvokeResolutionError(msg.trimEnd());
|
|
69184
69557
|
}
|
|
69185
69558
|
|
|
69186
|
-
//#endregion
|
|
69187
|
-
//#region src/utils/ecr-uri.ts
|
|
69188
|
-
/**
|
|
69189
|
-
* Matching the HOST half of an ECR image URI:
|
|
69190
|
-
* `<acct>.dkr.ecr.<region>.<urlSuffix>/`.
|
|
69191
|
-
*
|
|
69192
|
-
* The suffix is CAPTURED rather than spelled out (issue #1758): the previous
|
|
69193
|
-
* pattern hardcoded `amazonaws.com` with an optional `.cn` tail, so a `us-iso*`
|
|
69194
|
-
* registry (`c2s.ic.gov` / `sc2s.sgov.gov`) never matched and the caller
|
|
69195
|
-
* silently classified a real ECR image as a user-managed one — skipping the
|
|
69196
|
-
* `docker login` it needs.
|
|
69197
|
-
*
|
|
69198
|
-
* KNOWN BOUND (issue #1792): the `dkr.ecr` LABELS are still matched
|
|
69199
|
-
* case-sensitively, so `<acct>.DKR.ECR.<region>.<suffix>/` does not match at
|
|
69200
|
-
* all. Audited deliberately while fixing issue #1786 rather than missed — that
|
|
69201
|
-
* issue's normalization covers the captured region + suffix, i.e. the strict
|
|
69202
|
-
* CHECK, whereas widening the shape MATCHER makes cdkd `docker login` to a
|
|
69203
|
-
* host shape it previously skipped, which is a behavior expansion wanting its
|
|
69204
|
-
* own decision. The failure direction here is the safe one: NO credentials are
|
|
69205
|
-
* sent on any of the four call sites. Traced — `local run-task` and
|
|
69206
|
-
* `local invoke-agentcore` fall through to an anonymous `docker pull` that
|
|
69207
|
-
* fails, while `local invoke` and `local start-api` REFUSE before any pull.
|
|
69208
|
-
*/
|
|
69209
|
-
const ECR_URI_HOST_REGEX = /^(\d{12})\.dkr\.ecr\.([^.]+)\.([^/]+)\//;
|
|
69210
|
-
/**
|
|
69211
|
-
* A region segment cdkd is willing to treat as a region id.
|
|
69212
|
-
*
|
|
69213
|
-
* Case is not the only way the `startsWith` partition classification can be
|
|
69214
|
-
* side-stepped: any leading junk defeats it the same way, and the captured
|
|
69215
|
-
* segment does not stay inert — it becomes an `ECRClient({region})` and is
|
|
69216
|
-
* interpolated into the fallback `docker login` endpoint. Measured before this
|
|
69217
|
-
* guard: `…dkr.ecr. us-iso-east-1.amazonaws.com/…` (leading space) and a
|
|
69218
|
-
* combining-mark form both parsed, yielding regions `" us-iso-east-1"` and
|
|
69219
|
-
* `"us-i̇so-east-1"`. Neither leaks credentials — an ACCEPTED suffix is always
|
|
69220
|
-
* an AWS-owned domain — but a region id is ASCII alphanumeric plus `-`, so
|
|
69221
|
-
* anything else is a malformed host and refusing it keeps the classification
|
|
69222
|
-
* honest.
|
|
69223
|
-
*
|
|
69224
|
-
* TESTED AGAINST THE RAW SEGMENT, BEFORE `toLowerCase()`, and that order is the
|
|
69225
|
-
* whole point: `String.prototype.toLowerCase` performs full Unicode case
|
|
69226
|
-
* folding, so the Kelvin sign U+212A folds to a plain ASCII `k`. Checking the
|
|
69227
|
-
* FOLDED segment therefore accepts `us-eKst-1` and yields the region
|
|
69228
|
-
* `us-ekst-1` — a region the host does not name — which is precisely the
|
|
69229
|
-
* class of substitution this guard exists to refuse. Matching `[A-Za-z0-9-]`
|
|
69230
|
-
* on the raw capture keeps case-insensitivity (the point of issue #1786) while
|
|
69231
|
-
* admitting only characters that fold to themselves.
|
|
69232
|
-
*
|
|
69233
|
-
* ONE DELIBERATE SIDE EFFECT, called out because it is a withdrawal rather than
|
|
69234
|
-
* an addition: a malformed-region host is now classified as an ordinary public
|
|
69235
|
-
* image with NO cdkd-side signal at all — neither the ECR verdict nor the #1764
|
|
69236
|
-
* foreign-suffix diagnostic. Both arms change, not just the foreign one:
|
|
69237
|
-
* `<acct>.dkr.ecr.us_east_1.example.com/…` used to log the partition-gap
|
|
69238
|
-
* warning and now logs nothing, and `<acct>.dkr.ecr.us_east_1.amazonaws.com/…`
|
|
69239
|
-
* used to be ACCEPTED as ECR and is now refused. That is the correct verdict
|
|
69240
|
-
* rather than a regression — the diagnostic's whole subject is "this suffix does
|
|
69241
|
-
* not belong to its region's PARTITION", and a segment that is not a region id
|
|
69242
|
-
* has no partition to belong to, so reporting it would send the reader hunting
|
|
69243
|
-
* for a table entry that could never exist — but it does mean a typo'd region
|
|
69244
|
-
* gets no cdkd-side hint, only docker's own pull failure. Both arms are pinned
|
|
69245
|
-
* by tests so a future widening of the guard cannot flip them silently.
|
|
69246
|
-
*/
|
|
69247
|
-
const CANONICAL_REGION_SEGMENT = /^[A-Za-z0-9][A-Za-z0-9-]*$/;
|
|
69248
|
-
/**
|
|
69249
|
-
* A URL suffix cdkd is willing to compare against the partition table.
|
|
69250
|
-
*
|
|
69251
|
-
* The SAME raw-capture rule as the region, and for the same reason one layer
|
|
69252
|
-
* over: the suffix is folded before the comparison, so a code point folding
|
|
69253
|
-
* into ASCII can impersonate a partition's suffix. This became reachable the
|
|
69254
|
-
* moment issue #1764 landed `aws-isoe`'s `cloud.adc-e.uk` — that suffix
|
|
69255
|
-
* contains a `k`, so `…dkr.ecr.eu-isoe-west-1.cloud.adc-e.uK/…` (Kelvin
|
|
69256
|
-
* sign) folded onto it and was ACCEPTED. Not a credential leak, since UTS-46
|
|
69257
|
-
* maps U+212A to `k` too and the host resolves to the real AWS domain, but it
|
|
69258
|
-
* falsifies the invariant the region guard's comment above states, so both
|
|
69259
|
-
* captures are held to the same rule rather than only the one that was
|
|
69260
|
-
* measured first.
|
|
69261
|
-
*
|
|
69262
|
-
* Dots are admitted because a suffix is a dotted DNS name; every other
|
|
69263
|
-
* character is refused, so the accepted set stays exactly the partition
|
|
69264
|
-
* table's own literals.
|
|
69265
|
-
*/
|
|
69266
|
-
const CANONICAL_URL_SUFFIX = /^[A-Za-z0-9][A-Za-z0-9.-]*$/;
|
|
69267
|
-
/**
|
|
69268
|
-
* The ONE case-normalization boundary of this module (issue #1786).
|
|
69269
|
-
*
|
|
69270
|
-
* DNS is case-INSENSITIVE, so `US-ISO-EAST-1.amazonaws.com` and
|
|
69271
|
-
* `us-iso-east-1.amazonaws.com` name the same host — but both halves of the
|
|
69272
|
-
* #1758 suffix check were case-SENSITIVE, in OPPOSITE directions:
|
|
69273
|
-
*
|
|
69274
|
-
* - an upper-cased REGION fails every `startsWith` prefix test in
|
|
69275
|
-
* `derivePartitionAndUrlSuffix`, so it falls through to the commercial
|
|
69276
|
-
* partition whose suffix is `amazonaws.com` — which is exactly what an ISO
|
|
69277
|
-
* look-alike host carries, so the strict check ACCEPTED it (the bypass);
|
|
69278
|
-
* - an upper-cased region or SUFFIX on a GENUINE host stopped matching its own
|
|
69279
|
-
* partition's suffix, so a real ECR registry was REJECTED and the caller
|
|
69280
|
-
* silently classified it as a user-managed image — the #1758 regression, one
|
|
69281
|
-
* layer up.
|
|
69282
|
-
*
|
|
69283
|
-
* The normalization lives HERE rather than inside `derivePartitionAndUrlSuffix`
|
|
69284
|
-
* because this is the boundary where an untrusted, DNS-shaped string enters
|
|
69285
|
-
* cdkd, and keeping it in ONE function means both exported entry points below —
|
|
69286
|
-
* and the suffix COMPARISON itself — cannot drift apart.
|
|
69287
|
-
*
|
|
69288
|
-
* This does NOT duplicate `canonicalizeRegion`, which issue #1795 added inside
|
|
69289
|
-
* `derivePartitionAndUrlSuffix` so the mapping folds its own input. The two
|
|
69290
|
-
* answer different questions and both are needed: that one makes the partition
|
|
69291
|
-
* LOOKUP case-insensitive for every caller, while these guards decide whether
|
|
69292
|
-
* the captured segments are a region id and a URL suffix AT ALL. Folding alone
|
|
69293
|
-
* cannot do that — folding is precisely what turns `us-eKst-1` into the
|
|
69294
|
-
* plausible `us-ekst-1` (see the guards' own comments), so a fold-only helper
|
|
69295
|
-
* makes the substitution easier to reach, not harder. Double-folding is a
|
|
69296
|
-
* no-op, which is why the redundant `toLowerCase()` calls below are left in
|
|
69297
|
-
* rather than removed: they keep this function the single answer for the
|
|
69298
|
-
* comparison even if the helper's behaviour changes again.
|
|
69299
|
-
*
|
|
69300
|
-
* The derived suffix is lower-cased too. Every entry in the partition table is
|
|
69301
|
-
* a lower-case literal today, so that is a no-op; it is there so this function
|
|
69302
|
-
* stays the single answer even if that table ever gains a mixed-case entry.
|
|
69303
|
-
*/
|
|
69304
|
-
function matchEcrRegistryHost(imageUri) {
|
|
69305
|
-
const m = ECR_URI_HOST_REGEX.exec(imageUri);
|
|
69306
|
-
if (!m) return void 0;
|
|
69307
|
-
if (!CANONICAL_REGION_SEGMENT.test(m[2])) return void 0;
|
|
69308
|
-
if (!CANONICAL_URL_SUFFIX.test(m[3])) return void 0;
|
|
69309
|
-
const region = m[2].toLowerCase();
|
|
69310
|
-
return {
|
|
69311
|
-
accountId: m[1],
|
|
69312
|
-
region,
|
|
69313
|
-
suffix: m[3].toLowerCase(),
|
|
69314
|
-
expectedSuffix: derivePartitionAndUrlSuffix(region).urlSuffix.toLowerCase()
|
|
69315
|
-
};
|
|
69316
|
-
}
|
|
69317
|
-
/**
|
|
69318
|
-
* The account + region of an ECR image URI, or `undefined` when the URI is not
|
|
69319
|
-
* an ECR registry host for the region it names.
|
|
69320
|
-
*
|
|
69321
|
-
* Lives in `src/utils/` rather than beside its first caller because it has TWO
|
|
69322
|
-
* consumers in different layers — `src/local/ecr-puller.ts` (which needs the
|
|
69323
|
-
* `:tag` tail too) and `src/local/ecs-task-resolver.ts` (which classifies an
|
|
69324
|
-
* image that may carry a digest or no tag at all). Before issue #1758 they each
|
|
69325
|
-
* carried their own copy of the hardcoded commercial pattern, and fixing only
|
|
69326
|
-
* the pull path left `cdkd local run-task` broken outside the commercial
|
|
69327
|
-
* partition; ONE definition is what stops them drifting apart again.
|
|
69328
|
-
*
|
|
69329
|
-
* The module is deliberately free of AWS SDK imports, which is what lets
|
|
69330
|
-
* `ecs-task-resolver.ts` consume it without breaking the invariant that module
|
|
69331
|
-
* documents about itself (it resolves secrets through a separate module for
|
|
69332
|
-
* exactly that reason).
|
|
69333
|
-
*
|
|
69334
|
-
* The returned `region` is LOWER-CASED (issue #1786) — it is consumed as an SDK
|
|
69335
|
-
* client region by `ecr-puller.ts` and as the `region` of a `kind: 'ecr'` image
|
|
69336
|
-
* by `ecs-task-resolver.ts`, and the canonical spelling of an AWS region id is
|
|
69337
|
-
* lower case.
|
|
69338
|
-
*/
|
|
69339
|
-
function parseEcrRegistryHost(imageUri) {
|
|
69340
|
-
const m = matchEcrRegistryHost(imageUri);
|
|
69341
|
-
if (!m) return void 0;
|
|
69342
|
-
if (m.suffix !== m.expectedSuffix) return void 0;
|
|
69343
|
-
return {
|
|
69344
|
-
accountId: m.accountId,
|
|
69345
|
-
region: m.region
|
|
69346
|
-
};
|
|
69347
|
-
}
|
|
69348
|
-
/**
|
|
69349
|
-
* True when the URI has the ECR HOST SHAPE but its suffix does not belong to
|
|
69350
|
-
* the region it names — i.e. exactly the case {@link parseEcrRegistryHost}
|
|
69351
|
-
* rejects for a reason the caller may want to report.
|
|
69352
|
-
*
|
|
69353
|
-
* The two rejections are worth telling apart at a call site that degrades
|
|
69354
|
-
* silently: a genuinely public image and a registry in a partition
|
|
69355
|
-
* `derivePartitionAndUrlSuffix` does not know yet (issue #1764) both come back
|
|
69356
|
-
* `undefined`, and only the second is a cdkd gap rather than a user choice.
|
|
69357
|
-
*/
|
|
69358
|
-
function looksLikeEcrHostWithForeignSuffix(imageUri) {
|
|
69359
|
-
const m = matchEcrRegistryHost(imageUri);
|
|
69360
|
-
return m !== void 0 && m.suffix !== m.expectedSuffix;
|
|
69361
|
-
}
|
|
69362
|
-
|
|
69363
69559
|
//#endregion
|
|
69364
69560
|
//#region src/local/ecs-task-resolver.ts
|
|
69365
69561
|
function detectEcsImageResolutionNeeds(stack) {
|
|
@@ -78062,7 +78258,7 @@ function createMigrateCommand() {
|
|
|
78062
78258
|
*/
|
|
78063
78259
|
function buildProgram() {
|
|
78064
78260
|
const program = new Command();
|
|
78065
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.283.
|
|
78261
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.283.12");
|
|
78066
78262
|
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
78067
78263
|
const { profile } = actionCommand.optsWithGlobals();
|
|
78068
78264
|
if (profile !== void 0) process.env["AWS_PROFILE"] = profile;
|