@go-to-k/cdkd 0.290.42 → 0.290.44
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-Cms9Daqn.js → asg-provider-BwN09Zcn.js} +3 -3
- package/dist/{asg-provider-Cms9Daqn.js.map → asg-provider-BwN09Zcn.js.map} +1 -1
- package/dist/cli.js +2 -2
- package/dist/{deploy-engine-C1dgx124.js → deploy-engine-CMD4BtQe.js} +767 -38
- package/dist/deploy-engine-CMD4BtQe.js.map +1 -0
- package/dist/index.d.ts +16 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/{logger-D-2HeLmd.js → logger-DjBU2Ap3.js} +10 -1
- package/dist/logger-DjBU2Ap3.js.map +1 -0
- package/dist/{program-HtHlEaZm.js → program-CGhtrsw6.js} +278 -151
- package/dist/{program-HtHlEaZm.js.map → program-CGhtrsw6.js.map} +1 -1
- package/dist/{version-Divizw1k.js → version-4uhXCFhs.js} +2 -2
- package/dist/{version-Divizw1k.js.map → version-4uhXCFhs.js.map} +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-C1dgx124.js.map +0 -1
- package/dist/logger-D-2HeLmd.js.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { $ as yellow, $n as
|
|
2
|
-
import { C as displayAwsMessage, D as truncateCodePoints, E as isPasteableIdent, S as UNRENDERABLE, T as displaySafe, _ as withSkipPrefix, b as SECRET_REF_MAX_CODE_POINTS, c as PATTERN_B_NAME_OPTIONS, f as explicitNamePropertyFor, h as getCurrentStackName, i as reserveStdoutForPayload, l as PATTERN_B_NAME_PROPERTIES, m as generateResourceNameWithFallback, n as getLogger, o as runStackBuffered, p as generateResourceName, s as getLiveRenderer, u as PATTERN_B_RESOURCE_TYPES, v as withStackName, w as displayIdent, x as STACK_REF_MAX_CODE_POINTS } from "./logger-
|
|
1
|
+
import { $ as yellow, $i as AssemblyReader, $n as safeStackName, $r as assertAssetBucketRegion, $t as requireConfigObject, A as createPreDeleteFinalSnapshot, Aa as ResourceTimeoutError, Ai as resolveCaptureObservedState, An as malformedOutputsRefusalMessage, Ar as formatLockExpiry, At as carriesDynamicReference, B as isStatefulRecreateTargetSync, Ba as isMarkedNonRetryable, Bi as MIGRATE_TMP_PREFIX, Bn as refuseMalformedOrphansForDestroy, Br as isReadableBag, Bt as isSettledInstanceState, C as namesAnyDeclaredParameter, Ca as LocalInvokeBuildError$1, Ci as getDockerImageBySourceHash, Cn as isReadableResourceEntry, Cr as LockManager, Ct as slowCcOperationTimeoutMs, D as PRE_DELETE_SNAPSHOT_TYPES, Da as NestedStackChildDirectDestroyError, Di as getLegacyStateBucketName, Dn as malformedOrphanRecordsWarning, Dr as buildForceUnlockCommand, Dt as disableInstanceApiTermination, E as ATOMIC_FINAL_SNAPSHOT_TYPES, Ea as MalformedProducerRecordRefusalError, Ei as getDefaultStateBucketName, En as malformedLocalOutputsWarning, Er as UNREPRODUCIBLE_LOCK_CLAUSE, Et as deleteSkipReason, F as extractDeploymentEventError, Fa as SynthesisError, Fi as stateBucketExistenceConfirmed, Fn as malformedResourcesWarning, Fr as hasReadableExportSet, Ft as isImpossibleEmptyStoredAttribute, G as toCfnInteger, Ga as markRedactedCause, Gi as injectiveKeyPrefix, Gn as refuseMalformedResourcesForDestroy, Gr as stringifyValue, Gt as coerceCfnBoolean, H as WARM_THROUGHPUT_MEMBERS, Ha as isThrottlingError, Hi as uploadCfnTemplate, Hn as refuseMalformedOutputsForDestroy, Hr as orphansCarriedFrom, Ht as normalizeAwsTagsToCfn, I as makeSiblingClaimReader, Ii as warnDeprecatedNoPrefixCliFlag, In as malformedStateRefusalMessage, Ir as hasReasonlessBaselineRefusal, It as isNamelessDynamicReferenceError, J as bold, Ja as safeStringify, Ji as decodeListingKey, Jn as repairMalformedOutputsForReadOnly, Jr as createAssetRedirectResolver, Jt as configIntegerRefusal, K as toFiniteNumber, Ka as retryClassificationText, Ki as producerCoordinateKey, Kn as refuseMalformedState, Kr as WorkGraph, Kt as coerceCfnInteger, L as planOrphanAdoption, Li as ssmResolvedValueType, Ln as refuseDivergentRecordRegionForDestroy, Lr as hasUnverifiableParameterRefusal, Lt as isUnboundTemplateParameter, M as refusesFinalSnapshot, Ma as StackHasActiveImportsError, Mi as resolveStateBucketWithDefault, Mn as malformedRenderedContainersWarning, Mr as CUSTOM_RESOURCE_RESPONSE_PREFIX, Mt as cfnRefValueFromPhysicalId, N as unsupportedFinalSnapshotError, Na as StackTerminationProtectionError, Ni as resolveStateBucketWithDefaultAndSource, Nn as malformedResourceEntriesWarning, Nr as DEFAULT_STATE_PREFIX, Nt as coerceParameterTypedValue, O as buildFinalSnapshotIdentifier, Oa as PartialFailureError, Oi as resolveApp, On as malformedOrphansRefusalMessage, Or as buildLockContentionMessage, Ot as isTerminationProtectionPropagationError, P as makeCanonicalizePropertiesFn, Pi as resolveUseCdkBootstrapAssets, Pn as malformedResourcePropertiesWarning, Pr as exportNamesCarriedFrom, Pt as getAccountInfo, Q as red, Qi as derivePartitionAndUrlSuffix, Qn as safeRegion, Qr as BOOTSTRAP_MARKER_PREFIX, Qt as requireConfigArray, R as MULTI_REGION_RECREATE_BLOCKED_TYPES, Ra as normalizeAwsError, Ri as CFN_TEMPLATE_BODY_LIMIT, Rn as refuseMalformedNestedChildOutputs, Rr as importableOutputKeys, Rt as parameterTypeMayLoseSecretIdentity, S as computeParameterTaint, Sa as IntrinsicResolutionRefusalError, Si as AssetManifestLoader, Sn as hasReadableResources, Sr as scrubResourceRecord, St as isWaitAbandonedError, T as resourcesNamingDeclaredParameter, Ti as synthesisStatusMessage, Tn as malformedExportSourceWarning, Tr as rebuildClientForBucketRegion, Tt as deleteIndeterminateGuards, U as coerceWarmThroughput$1, Ua as isTransientServerError, Ui as CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION, Un as refuseMalformedResourceEntries, Ur as shouldRetainResource, Ut as resolveExplicitPhysicalId, V as renderStatefulReason, Va as isRetryableTransientError, Vi as findLargeInlineResources, Vn as refuseMalformedOutputs, Vr as orphansAfterRollback, Vt as WAFv2WebACLProvider, W as isWarmThroughputDecrease, Wa as markNonRetryable, Wi as injectiveKey, Wn as refuseMalformedResourcePropertiesForOrphan, Wr as AssetPublisher, Wt as assertRegionMatch, X as gray, Xi as PARTITION_TABLE, Xn as repairMalformedResourcePropertiesForReadOnly, Xr as rewriteTemplateAssetReferences, Xt as readConfigString, Y as cyan, Yi as expectedOwnerParam, Yn as repairMalformedResourceEntriesForReadOnly, Yr as loadPublishableAssetManifest, Yt as configStringRefusal, Z as green, Zi as canonicalizeRegion, Zn as repairMalformedResourcesForReadOnly, Zr as AssetModeResolver, Zt as replayWarn, _ as bagHoldsSecretExpression, _i as redactDockerArgvValues, _n as UNREADABLE_ORPHANS_CONTAINER_ROW, _r as maskSecretsInError, _t as endCommandInterruptScope, a as DeploymentEventsStore, aa as nullPrototypeRecord, ai as validateAssetBucketName, an as s3BucketDomainName, ar as TemplateParser, at as secretBearingStateKeyWarning, b as mergeNoChangeOutputs, ba as DeployCancelledError, bi as escapeRegExp, bn as hasReadableOrphans, br as recoverMaskedOutput, bt as startInterruptWatch, c as replayFailedOperations, ca as resolveAssemblyPath, ci as buildDockerImage, cn as s3BucketWebsiteUrl, cr as STATE_SOURCED_READBACK_RULES, ct as IAMRoleProvider, d as updatePartialMessage, da as AwsClients, di as describeDockerExecFailure, dn as applyCallerIdentityCredentials, dr as createSecretMasker, dt as ProviderRegistry, ea as failedStageNote, ei as ensureAssetStorage, en as requireConfigString, er as findActionableSilentDrops, et as collectDeclaredOutputNames, f as updatePartialReason, fa as getAwsClients, fi as describeDockerFailure, fn as callerEnvCredentials, fr as dynamicReferenceTokens, ft as wouldReturnToSdkProvider, g as computeImplicitDeleteEdges, ga as CdkdError, gi as partitionSensitiveEnv, gn as STATE_RESOURCES_MALFORMED, gr as markSameGenerationBag, gt as beginCommandInterruptScope, h as IMPLICIT_DELETE_DEPENDENCIES, hi as getDockerCmd, hn as INTRINSIC_KEYS, hr as isSingleDynamicReferenceToken, ht as maskerOrIdentity, i as DeploymentEventsReader, ia as hasPlainPrototype, ii as readBootstrapMarkerBody, in as s3BucketArn, ir as DagBuilder, it as secretBearing, j as isFinalSnapshotError, ja as ResourceUpdateNotSupportedError, ji as resolveSkipPrefix, jn as malformedOutputsWarning, jr as shellQuote, jt as carriesFetchableDynamicReference, k as ccRoutedFinalSnapshotError, ka as ProvisioningError, ki as resolveAutoAssetStorage, kn as malformedOrphansWarning, kr as forceQuitRecoveryClause, kt as IntrinsicFunctionResolver, l as replayRollback, li as resolveDockerContextDirectory, ln as applyRoleArnIfSet, lr as TEMPLATE_SOURCED_RULES, lt as collectInlinePolicyNamesManagedBySiblings, m as maskingRetryLogger, ma as setAwsClients, mi as formatDockerLoginError, mn as DiffCalculator, mr as identityKeyFor, mt as maskDeep, n as DEFAULT_RESOURCE_WARN_AFTER_MS, na as defineOwnKey, ni as isCrossRegionRedirect, nn as producerRegionsFromState, nr as describeTypeWithThrottleRetry, nt as exportAliasCollisionScrubWarning, o as planFailedOps, oa as ownValue, oi as validateContainerRepoName, on as s3BucketDualStackDomainName, or as STATE_SOURCED_BASELINE_RULES, ot as secretSafeKeyDisplay, p as withResourceDeadline, pa as resetAwsClients, pi as dockerSpawnEnvWithSensitive, pn as envCredentialsAreAssumedRole, pr as errorCauseChain, pt as createMaskedRetryLogger, q as formatResourceLine, qa as describeAwsFailure, qi as producerRecordKey, qn as repairMalformedOrphansForReadOnly, qr as buildAssetRedirectMap, qt as configBooleanRefusal, r as DeployEngine, ra as hasOwnKey, ri as parseBootstrapMarker, rn as withSharedDrainBudget, rr as withRetry, rt as isExportAliasCollision, s as planRollback, sa as renderAssemblyPathEscape, si as buildDenyExternalAccessPolicy, sn as s3BucketRegionalDomainName, sr as STATE_SOURCED_CROSS_GENERATION_RULES, st as getCurrentResourceSecrets, t as DEFAULT_RESOURCE_TIMEOUT_MS, ta as processStackMessages, ti as getBootstrapMarkerKey, tn as classifyReplaySecretRegion, tr as findSilentDropProperties, tt as collectPublishedOutputNames, u as resolveReplacementOldType, ua as resolveBucketRegion, ui as describeDockerCapturedOutput, un as AWS_CREDENTIAL_ENV_KEYS, ur as carriesSecretMask, ut as clearOnUpdateRemoval, v as isSecretBearingReferenceString, va as CrossAccountSecretRefusalError, vi as runDockerForeground, vn as UNREADABLE_RESOURCES_MAP_ROW, vr as maskSecretsInText, vt as interruptWatchListenerCount, w as reachableDivergentParameters, wa as LocalStartServiceError, wi as Synthesizer, wn as malformedExportNamesWarning, wr as S3StateBackend, wt as UNSPECIFIED_SKIP_REASON, x as bindingSkippedOutputs, xa as DynamicReferenceRegionAmbiguousError, xi as stripControlChars, xn as hasReadableOutputs, xr as redactSecretsForState, xt as CloudControlProvider, y as keptWholeReasonText, yi as runDockerStreaming, yn as displayLogicalId, yr as recordMaskOnlyValue, yt as isInterruptedWaitError, z as isStatefulRecreateTargetForReplace, za as withErrorHandling, zi as CFN_TEMPLATE_URL_LIMIT, zn as refuseMalformedOrphans, zr as importableOutputs, zt as refStateLookupFromResource } from "./deploy-engine-CMD4BtQe.js";
|
|
2
|
+
import { C as displayAwsMessage, D as truncateCodePoints, E as isPasteableIdent, S as UNRENDERABLE, T as displaySafe, _ as withSkipPrefix, b as SECRET_REF_MAX_CODE_POINTS, c as PATTERN_B_NAME_OPTIONS, f as explicitNamePropertyFor, h as getCurrentStackName, i as reserveStdoutForPayload, l as PATTERN_B_NAME_PROPERTIES, m as generateResourceNameWithFallback, n as getLogger, o as runStackBuffered, p as generateResourceName, s as getLiveRenderer, u as PATTERN_B_RESOURCE_TYPES, v as withStackName, w as displayIdent, x as STACK_REF_MAX_CODE_POINTS } from "./logger-DjBU2Ap3.js";
|
|
3
3
|
import { n as awsClientDefaults } from "./aws-client-defaults-BoIxyCir.js";
|
|
4
|
-
import { t as getCdkdVersion } from "./version-
|
|
5
|
-
import { a as renderDisableCommand, i as protectedReplacementAdvice, r as UNNAMEABLE_ID_CLAUSE, t as ASGProvider } from "./asg-provider-
|
|
4
|
+
import { t as getCdkdVersion } from "./version-4uhXCFhs.js";
|
|
5
|
+
import { a as renderDisableCommand, i as protectedReplacementAdvice, r as UNNAMEABLE_ID_CLAUSE, t as ASGProvider } from "./asg-provider-BwN09Zcn.js";
|
|
6
6
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
7
7
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
8
8
|
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, NotFound, 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";
|
|
@@ -34,7 +34,6 @@ import { tmpdir } from "node:os";
|
|
|
34
34
|
import { AssociateVPCWithHostedZoneCommand, ChangeResourceRecordSetsCommand, ChangeTagsForResourceCommand, CreateHostedZoneCommand, CreateQueryLoggingConfigCommand, DeleteHostedZoneCommand, DeleteQueryLoggingConfigCommand, DisassociateVPCFromHostedZoneCommand, GetHostedZoneCommand, HostedZoneAlreadyExists, ListHostedZonesByNameCommand, ListQueryLoggingConfigsCommand, ListResourceRecordSetsCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$8, Route53Client, UpdateHostedZoneCommentCommand, UpdateHostedZoneFeaturesCommand } from "@aws-sdk/client-route-53";
|
|
35
35
|
import { AddTagsCommand, CreateListenerCommand, CreateLoadBalancerCommand, CreateTargetGroupCommand, DeleteListenerCommand, DeleteLoadBalancerCommand, DeleteTargetGroupCommand, DeregisterTargetsCommand, DescribeCapacityReservationCommand, DescribeListenerAttributesCommand, DescribeListenersCommand, DescribeLoadBalancerAttributesCommand, DescribeLoadBalancersCommand, DescribeTagsCommand, DescribeTargetGroupAttributesCommand, DescribeTargetGroupsCommand, DescribeTargetHealthCommand, ElasticLoadBalancingV2Client, ModifyCapacityReservationCommand, ModifyIpPoolsCommand, ModifyListenerAttributesCommand, ModifyListenerCommand, ModifyLoadBalancerAttributesCommand, ModifyTargetGroupAttributesCommand, ModifyTargetGroupCommand, RegisterTargetsCommand, RemoveTagsCommand, SetIpAddressTypeCommand, SetSecurityGroupsCommand, SetSubnetsCommand, waitUntilLoadBalancerAvailable } from "@aws-sdk/client-elastic-load-balancing-v2";
|
|
36
36
|
import { CreateAliasCommand, CreateKeyCommand, DeleteAliasCommand, DescribeKeyCommand, DisableKeyCommand, DisableKeyRotationCommand, EnableKeyCommand, EnableKeyRotationCommand, GetKeyPolicyCommand, GetKeyRotationStatusCommand, KMSClient, ListAliasesCommand, ListResourceTagsCommand, NotFoundException as NotFoundException$2, PutKeyPolicyCommand, ScheduleKeyDeletionCommand, TagResourceCommand as TagResourceCommand$11, UntagResourceCommand as UntagResourceCommand$11, UpdateAliasCommand, UpdateKeyDescriptionCommand } from "@aws-sdk/client-kms";
|
|
37
|
-
import { join as join$1 } from "path";
|
|
38
37
|
import { BatchDeleteImageCommand, CreateRepositoryCommand, DeleteLifecyclePolicyCommand, DeleteRepositoryCommand, DeleteRepositoryPolicyCommand, DescribeImagesCommand as DescribeImagesCommand$1, DescribeRepositoriesCommand, ECRClient, GetAuthorizationTokenCommand, GetLifecyclePolicyCommand, LifecyclePolicyNotFoundException, ListTagsForResourceCommand as ListTagsForResourceCommand$9, PutImageScanningConfigurationCommand, PutImageTagMutabilityCommand, PutLifecyclePolicyCommand, RepositoryNotEmptyException, RepositoryNotFoundException, SetRepositoryPolicyCommand, TagResourceCommand as TagResourceCommand$12, UntagResourceCommand as UntagResourceCommand$12 } from "@aws-sdk/client-ecr";
|
|
39
38
|
import { isDeepStrictEqual, promisify } from "node:util";
|
|
40
39
|
import graphlib from "graphlib";
|
|
@@ -42,7 +41,6 @@ import { AddTagsToResourceCommand as AddTagsToResourceCommand$2, CreateDBCluster
|
|
|
42
41
|
import { Command, InvalidArgumentError, Option } from "commander";
|
|
43
42
|
import * as readline$1 from "node:readline/promises";
|
|
44
43
|
import readline from "node:readline/promises";
|
|
45
|
-
import { writeFileSync as writeFileSync$1 } from "fs";
|
|
46
44
|
import { Document, Pair, Scalar, YAMLMap, YAMLSeq, isScalar, parse, stringify, visit } from "yaml";
|
|
47
45
|
import * as zlib from "node:zlib";
|
|
48
46
|
import { ApplicationAutoScalingClient, DeleteScalingPolicyCommand, DeregisterScalableTargetCommand, DescribeScalableTargetsCommand, DescribeScalingPoliciesCommand, PutScalingPolicyCommand, RegisterScalableTargetCommand } from "@aws-sdk/client-application-auto-scaling";
|
|
@@ -2265,7 +2263,35 @@ function countDeployableResources(template) {
|
|
|
2265
2263
|
return Object.values(template.Resources ?? {}).filter((r) => r.Type !== "AWS::CDK::Metadata").length;
|
|
2266
2264
|
}
|
|
2267
2265
|
/**
|
|
2268
|
-
*
|
|
2266
|
+
* Where `cdkd synth --verbose` writes one stack's template, refusing a path
|
|
2267
|
+
* that leaves `outputDir` (issue
|
|
2268
|
+
* [#3489](https://github.com/go-to-k/cdkd/issues/3489)).
|
|
2269
|
+
*
|
|
2270
|
+
* `stackName` is the assembly manifest's own `properties.stackName`, falling
|
|
2271
|
+
* back to the artifact id, so it is chosen by whoever wrote the assembly — and
|
|
2272
|
+
* this is the ONE place cdkd turns such a string into a path it WRITES. A
|
|
2273
|
+
* `../../../..`-bearing name would put attacker-chosen JSON outside the output
|
|
2274
|
+
* directory, clobbering whatever `<name>.template.json` it can reach.
|
|
2275
|
+
*
|
|
2276
|
+
* Exported for unit testing.
|
|
2277
|
+
*/
|
|
2278
|
+
function resolveVerboseTemplatePath(outputDir, stackName) {
|
|
2279
|
+
const resolved = resolveAssemblyPath(outputDir, `${stackName}.template.json`);
|
|
2280
|
+
if (!resolved.contained) throw new SynthesisError(`Stack '${displaySafe(stackName)}' would write its template to a path that ${renderAssemblyPathEscape(resolved, outputDir, "write it")}`);
|
|
2281
|
+
let linked = false;
|
|
2282
|
+
try {
|
|
2283
|
+
linked = lstatSync(resolved.path).isSymbolicLink();
|
|
2284
|
+
} catch {}
|
|
2285
|
+
if (linked) throw new SynthesisError(`Stack '${displaySafe(stackName)}' would write its template over a symbolic link at '${displaySafe(resolved.path)}'. cdkd writes a stack's template as a regular file beside the assembly; a symbolic link there redirects the write to wherever it points, so this indicates the synth output was hand-modified or generated by a non-CDK toolchain. Refusing to write it.`);
|
|
2286
|
+
return resolved.path;
|
|
2287
|
+
}
|
|
2288
|
+
/**
|
|
2289
|
+
* Synth command implementation.
|
|
2290
|
+
*
|
|
2291
|
+
* Exported for the wiring test that pins `--verbose` writing through
|
|
2292
|
+
* {@link resolveVerboseTemplatePath} rather than a bare `path.join`
|
|
2293
|
+
* (go-to-k/cdkd#3489): the guard has its own cases, but reverting the CALL
|
|
2294
|
+
* SITE left the suite green.
|
|
2269
2295
|
*/
|
|
2270
2296
|
async function synthCommand(options) {
|
|
2271
2297
|
const logger = getLogger();
|
|
@@ -2311,9 +2337,9 @@ async function synthCommand(options) {
|
|
|
2311
2337
|
logger.info(` - Outputs: ${outputCount}`);
|
|
2312
2338
|
logger.info(` - Has assets: ${assemblyReader.hasAssets(stack) ? "Yes" : "No"}`);
|
|
2313
2339
|
if (options.verbose) {
|
|
2314
|
-
const templatePath =
|
|
2315
|
-
writeFileSync
|
|
2316
|
-
logger.debug(` - Template written to: ${templatePath}`);
|
|
2340
|
+
const templatePath = resolveVerboseTemplatePath(options.output, stack.stackName);
|
|
2341
|
+
writeFileSync(templatePath, JSON.stringify(stack.template, null, 2));
|
|
2342
|
+
logger.debug(` - Template written to: ${displaySafe(templatePath)}`);
|
|
2317
2343
|
}
|
|
2318
2344
|
}
|
|
2319
2345
|
logger.info(`\nOutput: ${assemblyDir}`);
|
|
@@ -2335,6 +2361,25 @@ function createSynthCommand() {
|
|
|
2335
2361
|
|
|
2336
2362
|
//#endregion
|
|
2337
2363
|
//#region src/cli/stack-matcher.ts
|
|
2364
|
+
/**
|
|
2365
|
+
* Match stacks against user-supplied name patterns.
|
|
2366
|
+
*
|
|
2367
|
+
* Patterns are evaluated against two fields:
|
|
2368
|
+
*
|
|
2369
|
+
* - `stackName` — the physical CloudFormation stack name (e.g. `MyStage-MyStack`)
|
|
2370
|
+
* - `displayName` — the hierarchical CDK path (e.g. `MyStage/MyStack`); falls
|
|
2371
|
+
* back to `stackName` when the assembly does not carry one
|
|
2372
|
+
*
|
|
2373
|
+
* Routing is decided by whether the pattern contains `/`:
|
|
2374
|
+
*
|
|
2375
|
+
* - Pattern contains `/` → matched only against `displayName` (a `/` cannot
|
|
2376
|
+
* appear in a CloudFormation stack name, so this is unambiguous)
|
|
2377
|
+
* - Pattern contains no `/` → matched only against `stackName`
|
|
2378
|
+
*
|
|
2379
|
+
* Wildcards (`*`) are supported in either case. Results are de-duplicated by
|
|
2380
|
+
* `stackName`, so a pattern that incidentally matches the same stack via both
|
|
2381
|
+
* fields is returned only once.
|
|
2382
|
+
*/
|
|
2338
2383
|
function matchStacks(stacks, patterns) {
|
|
2339
2384
|
if (patterns.length === 0) return [];
|
|
2340
2385
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -2349,10 +2394,57 @@ function matchStacks(stacks, patterns) {
|
|
|
2349
2394
|
* Render a stack for diagnostic messages. When `displayName` differs from the
|
|
2350
2395
|
* physical name, both are shown so the user can see which forms are valid as
|
|
2351
2396
|
* patterns (e.g. `MyStage-Api (MyStage/Api)`).
|
|
2397
|
+
*
|
|
2398
|
+
* Both names come from the Cloud Assembly, and every one of this helper's
|
|
2399
|
+
* render sites interpolates the result into PROSE cdkd authors — `Available:
|
|
2400
|
+
* ...`, `Multiple stacks found: ...`, `Publishing assets for stack: ...`, the
|
|
2401
|
+
* last of which prints on a normal run rather than only on an error. So they
|
|
2402
|
+
* go through `displayIdent` ([#3277](https://github.com/go-to-k/cdkd/issues/3277)):
|
|
2403
|
+
* a `stackName` carrying `ESC [ 2 K` plus a carriage return ERASES cdkd's own
|
|
2404
|
+
* line and a newline forges a second one, and a value carrying quotes and
|
|
2405
|
+
* periods writes a cdkd-sounding clause.
|
|
2406
|
+
*
|
|
2407
|
+
* It is the identity on every legitimate `stackName` — a CloudFormation stack
|
|
2408
|
+
* name is `[A-Za-z0-9-]`, inside `PLAIN_IDENT`. It is NOT the identity on
|
|
2409
|
+
* every legitimate `displayName`: CDK sets that to the construct path, and
|
|
2410
|
+
* `constructs` rewrites only `/` in an id, so `new Stack(app, 'My Stack')` is
|
|
2411
|
+
* legal and prints `MyStack ("My Stack")`. Those quotes are not part of any
|
|
2412
|
+
* pattern, in a clause whose job is to say which forms ARE valid as patterns —
|
|
2413
|
+
* accepted, because the alternative is `displaySafe`, which passes the quotes
|
|
2414
|
+
* and spaces a crafted value needs, and the pattern still matches the RAW
|
|
2415
|
+
* name either way (`matchStacks` never sees this rendering).
|
|
2416
|
+
*
|
|
2417
|
+
* Sanitizing HERE rather than at the call sites is what keeps the rule whole:
|
|
2418
|
+
* the sites include `scrub.ts` and `destroy.ts`, which this change does not
|
|
2419
|
+
* touch, and a rule widened by hand is how the class survived being closed
|
|
2420
|
+
* twice already.
|
|
2352
2421
|
*/
|
|
2353
2422
|
function describeStack(stack) {
|
|
2354
|
-
|
|
2355
|
-
return stack.
|
|
2423
|
+
const name = displayIdent(stack.stackName, { maxCodePoints: STACK_REF_MAX_CODE_POINTS });
|
|
2424
|
+
if (stack.displayName && stack.displayName !== stack.stackName) return `${name} (${displayIdent(stack.displayName, { maxCodePoints: STACK_REF_MAX_CODE_POINTS })})`;
|
|
2425
|
+
return name;
|
|
2426
|
+
}
|
|
2427
|
+
/**
|
|
2428
|
+
* The message a command raises when SELECTION came back empty — shared by
|
|
2429
|
+
* `deploy`, `diff`, `list` and `publish-assets`, which all built the identical
|
|
2430
|
+
* string by hand.
|
|
2431
|
+
*
|
|
2432
|
+
* `assembly` is required AND so is its `failedStages` member, which is the
|
|
2433
|
+
* point: a Stage that failed to load dropped every stack under it from
|
|
2434
|
+
* `available`, so "no stacks matching" names the wrong problem unless the
|
|
2435
|
+
* failure is reported with it (issue
|
|
2436
|
+
* [#3482](https://github.com/go-to-k/cdkd/issues/3482)). A REQUIRED member,
|
|
2437
|
+
* not an optional one — `{ failedStages?: ... }` accepts `{}`, so it fences
|
|
2438
|
+
* the argument while leaving the content unfenced, which is the wiring hole
|
|
2439
|
+
* this signature exists to close. `SynthesisResult.failedStages` is required
|
|
2440
|
+
* for the same reason, so passing the result satisfies it and an ad-hoc `{}`
|
|
2441
|
+
* does not.
|
|
2442
|
+
*
|
|
2443
|
+
* What it does NOT fence: a caller passing a STALE or empty list. That is
|
|
2444
|
+
* behaviour, not shape, and it is covered by a wiring test per command.
|
|
2445
|
+
*/
|
|
2446
|
+
function renderNoStackMatch(patterns, available, assembly) {
|
|
2447
|
+
return (patterns.length > 0 ? `No stacks matching ${patterns.join(", ")} found in assembly. ` + (available.length > 0 ? `Available: ${available.map(describeStack).join(", ")}` : "The assembly has no stacks") : "No stacks found in assembly") + failedStageNote(patterns, assembly.failedStages);
|
|
2356
2448
|
}
|
|
2357
2449
|
function stackMatchesPattern(stack, pattern) {
|
|
2358
2450
|
const target = pattern.includes("/") ? stack.displayName ?? stack.stackName : stack.stackName;
|
|
@@ -2430,10 +2522,11 @@ async function listCommand(patterns, options) {
|
|
|
2430
2522
|
...Object.keys(context).length > 0 && { context },
|
|
2431
2523
|
deferMacroExpansion: true
|
|
2432
2524
|
};
|
|
2433
|
-
const
|
|
2434
|
-
|
|
2525
|
+
const result = await synthesizer.synthesize(synthOptions);
|
|
2526
|
+
const allStacks = result.stacks;
|
|
2527
|
+
if (allStacks.length === 0) throw new Error(renderNoStackMatch(patterns, allStacks, result));
|
|
2435
2528
|
const selected = patterns.length > 0 ? matchStacks(allStacks, patterns) : allStacks;
|
|
2436
|
-
if (selected.length === 0) throw new Error(
|
|
2529
|
+
if (selected.length === 0) throw new Error(renderNoStackMatch(patterns, allStacks, result));
|
|
2437
2530
|
const sorted = sortByDependency(selected);
|
|
2438
2531
|
if (options.long) {
|
|
2439
2532
|
emitStructured(sorted.map((s) => toLongRecord(s, options.showDependencies)), options.json);
|
|
@@ -3444,7 +3537,7 @@ function templateIdentity(templatePath) {
|
|
|
3444
3537
|
const dir = path.dirname(templatePath);
|
|
3445
3538
|
let realDir;
|
|
3446
3539
|
try {
|
|
3447
|
-
realDir = fs.realpathSync(dir);
|
|
3540
|
+
realDir = fs.realpathSync.native(dir);
|
|
3448
3541
|
} catch {
|
|
3449
3542
|
realDir = path.resolve(dir);
|
|
3450
3543
|
}
|
|
@@ -3567,7 +3660,16 @@ function findNestedTemplateTreeDefect(nestedTemplates, ancestorTemplatePaths = [
|
|
|
3567
3660
|
logicalId: row.logicalId,
|
|
3568
3661
|
assetPath: row.assetPath
|
|
3569
3662
|
};
|
|
3570
|
-
const
|
|
3663
|
+
const resolved = resolveAssemblyPath(dir, row.assetPath);
|
|
3664
|
+
if (!resolved.contained) return {
|
|
3665
|
+
kind: "escaping-path",
|
|
3666
|
+
chain: [...chain],
|
|
3667
|
+
logicalId: row.logicalId,
|
|
3668
|
+
assetPath: row.assetPath,
|
|
3669
|
+
escape: resolved,
|
|
3670
|
+
dir
|
|
3671
|
+
};
|
|
3672
|
+
const defect = visit(row.logicalId, resolved.path);
|
|
3571
3673
|
if (defect) return defect;
|
|
3572
3674
|
}
|
|
3573
3675
|
} finally {
|
|
@@ -3626,6 +3728,7 @@ function renderNestedTemplateTreeDefect(defect, stackName, action) {
|
|
|
3626
3728
|
}
|
|
3627
3729
|
if (defect.kind === "too-large") return `The nested template tree under stack '${displaySafe(stackName)}' has more than ${MAX_ROWS_FOLLOWED} nested-stack rows to follow (the walk stopped at ${renderChain(defect.chain)}). That is far beyond any CDK-generated assembly. Symlinked directories can give one template file many paths, which multiplies the tree without ever repeating on one chain. Refusing to ${action}.`;
|
|
3628
3730
|
if (defect.kind === "too-deep") return `The nested template tree under stack '${displaySafe(stackName)}' nests more than ${512} levels deep: ${renderChain(defect.chain)}. No tree that deep can deploy, because each level lengthens the child's state key and S3 caps a key at 1024 bytes. ${provenance}`;
|
|
3731
|
+
if (defect.kind === "escaping-path") return `The nested template tree under stack '${displaySafe(stackName)}' has nested stack '${displaySafe(defect.logicalId)}' (reached through ${renderChain(defect.chain)}) with Metadata['aws:asset:path']='${displaySafe(defect.assetPath)}' which ${renderAssemblyPathEscape(defect.escape, defect.dir, action)}`;
|
|
3629
3732
|
return `The nested template tree under stack '${displaySafe(stackName)}' has nested stack '${displaySafe(defect.logicalId)}' (reached through ${renderChain(defect.chain)}) with Metadata['aws:asset:path']='${displaySafe(defect.assetPath)}' which is absolute. ${provenance}`;
|
|
3630
3733
|
}
|
|
3631
3734
|
|
|
@@ -63959,6 +64062,7 @@ async function runDestroyForStack(stackName, state, ctx) {
|
|
|
63959
64062
|
const guardIndeterminateTargets = /* @__PURE__ */ new Set();
|
|
63960
64063
|
const regionForState = state.region ?? ctx.baseRegion;
|
|
63961
64064
|
refuseMalformedResourcesForDestroy(state, stackName, regionForState);
|
|
64065
|
+
refuseMalformedOrphansForDestroy(state, stackName, regionForState);
|
|
63962
64066
|
refuseDivergentRecordRegionForDestroy(state, stackName, regionForState, ctx.divergentBodyRegion);
|
|
63963
64067
|
const resourceCount = Object.keys(state.resources).length;
|
|
63964
64068
|
const orphanCount = (state.orphans ?? []).length;
|
|
@@ -64009,7 +64113,10 @@ async function runDestroyForStack(stackName, state, ctx) {
|
|
|
64009
64113
|
emptyLockHeld = true;
|
|
64010
64114
|
try {
|
|
64011
64115
|
const recheck = await ctx.stateBackend.getState(stackName, regionForState);
|
|
64012
|
-
if (recheck)
|
|
64116
|
+
if (recheck) {
|
|
64117
|
+
refuseMalformedResourcesForDestroy(recheck.state, stackName, regionForState);
|
|
64118
|
+
refuseMalformedOrphansForDestroy(recheck.state, stackName, regionForState);
|
|
64119
|
+
}
|
|
64013
64120
|
const recheckResources = recheck ? Object.keys(recheck.state.resources).length : 0;
|
|
64014
64121
|
const recheckOrphans = recheck ? (recheck.state.orphans ?? []).length : 0;
|
|
64015
64122
|
if (!(!recheck || recheckResources === 0 && recheckOrphans === 0)) throw new Error(`Stack '${stackName}' (${regionForState}) was empty when this run started but now has ${recheckResources} resource(s) and ${recheckOrphans} rollback-orphaned resource(s) — another cdkd process wrote to it. Re-run the destroy to act on the current state.`);
|
|
@@ -64984,7 +65091,9 @@ var NestedStackProvider = class {
|
|
|
64984
65091
|
const result = Object.create(null);
|
|
64985
65092
|
for (const { logicalId: grandLogicalId, assetPath } of listNestedTemplateRows(childTemplate)) {
|
|
64986
65093
|
if (isAbsoluteCrossPlatform$1(assetPath)) throw markNonRetryable(/* @__PURE__ */ new Error(`NestedStackProvider: nested-stack '${displaySafe(grandLogicalId)}' has Metadata['aws:asset:path']='${displaySafe(assetPath)}' which is absolute. CDK emits relative asset paths for nested templates; an absolute path indicates the synth output was hand-modified or generated by a non-CDK toolchain. Refusing to load.`));
|
|
64987
|
-
|
|
65094
|
+
const resolved = resolveAssemblyPath(dir, assetPath);
|
|
65095
|
+
if (!resolved.contained) throw markNonRetryable(/* @__PURE__ */ new Error(`NestedStackProvider: nested-stack '${displaySafe(grandLogicalId)}' has Metadata['aws:asset:path']='${displaySafe(assetPath)}' which ${renderAssemblyPathEscape(resolved, dir)}`));
|
|
65096
|
+
result[grandLogicalId] = resolved.path;
|
|
64988
65097
|
}
|
|
64989
65098
|
return result;
|
|
64990
65099
|
}
|
|
@@ -65996,11 +66105,12 @@ async function deployCommand(stacks, options) {
|
|
|
65996
66105
|
logger.debug(`Found ${allStacks.length} stack(s) in assembly`);
|
|
65997
66106
|
const stackPatterns = stacks.length > 0 ? stacks : options.stack ? [options.stack] : [];
|
|
65998
66107
|
let targetStacks;
|
|
66108
|
+
if (allStacks.length === 0) throw new Error(renderNoStackMatch(stackPatterns, allStacks, result));
|
|
65999
66109
|
if (options.all) targetStacks = allStacks;
|
|
66000
66110
|
else if (stackPatterns.length > 0) targetStacks = matchStacks(allStacks, stackPatterns);
|
|
66001
66111
|
else if (allStacks.length === 1) targetStacks = allStacks;
|
|
66002
66112
|
else throw new Error(`Multiple stacks found: ${allStacks.map(describeStack).join(", ")}. Specify stack name(s) or use --all`);
|
|
66003
|
-
if (targetStacks.length === 0) throw new Error(stackPatterns
|
|
66113
|
+
if (targetStacks.length === 0) throw new Error(renderNoStackMatch(stackPatterns, allStacks, result));
|
|
66004
66114
|
const inferredCrossStackDeps = inferCrossStackStackDeps(allStacks);
|
|
66005
66115
|
const effectiveStackDeps = (stackName, deps) => {
|
|
66006
66116
|
const union = new Set(deps);
|
|
@@ -66097,7 +66207,8 @@ async function deployCommand(stacks, options) {
|
|
|
66097
66207
|
region: assetRegion,
|
|
66098
66208
|
...options.profile && { profile: options.profile },
|
|
66099
66209
|
nodePrefix: `${stack.stackName}:`,
|
|
66100
|
-
...redirect && { redirect }
|
|
66210
|
+
...redirect && { redirect },
|
|
66211
|
+
...stack.assetOutdir !== void 0 && { assetOutdir: stack.assetOutdir }
|
|
66101
66212
|
});
|
|
66102
66213
|
for (const id of nodeIds) stackDeps.add(id);
|
|
66103
66214
|
}
|
|
@@ -66248,7 +66359,7 @@ async function deployCommand(stacks, options) {
|
|
|
66248
66359
|
awsClients: stackAwsClients,
|
|
66249
66360
|
stateBucket,
|
|
66250
66361
|
exportIndexStore,
|
|
66251
|
-
nestedTemplates: stackInfo.nestedTemplates ??
|
|
66362
|
+
nestedTemplates: stackInfo.nestedTemplates ?? nullPrototypeRecord(),
|
|
66252
66363
|
dagBuilder,
|
|
66253
66364
|
diffCalculator,
|
|
66254
66365
|
options: deployEngineOptions,
|
|
@@ -67863,13 +67974,15 @@ function readNestedTemplate(templatePath) {
|
|
|
67863
67974
|
*/
|
|
67864
67975
|
function indexNestedChildTemplates(template, templatePath) {
|
|
67865
67976
|
const dir = path.dirname(templatePath);
|
|
67866
|
-
const result =
|
|
67977
|
+
const result = nullPrototypeRecord();
|
|
67867
67978
|
for (const [logicalId, resource] of Object.entries(template.Resources ?? {})) {
|
|
67868
67979
|
if (resource?.Type !== "AWS::CloudFormation::Stack") continue;
|
|
67869
67980
|
const assetPath = resource.Metadata?.["aws:asset:path"];
|
|
67870
67981
|
if (typeof assetPath !== "string" || assetPath.length === 0) continue;
|
|
67871
67982
|
if (isAbsoluteCrossPlatform(assetPath)) throw new Error(`Nested stack '${displaySafe(logicalId)}' has Metadata['aws:asset:path']='${displaySafe(assetPath)}' which is absolute. CDK emits relative asset paths for nested templates; an absolute path indicates the synth output was hand-modified or generated by a non-CDK toolchain. Refusing to load.`);
|
|
67872
|
-
|
|
67983
|
+
const resolved = resolveAssemblyPath(dir, assetPath);
|
|
67984
|
+
if (!resolved.contained) throw new Error(`Nested stack '${displaySafe(logicalId)}' has Metadata['aws:asset:path']='${displaySafe(assetPath)}' which ${renderAssemblyPathEscape(resolved, dir)}`);
|
|
67985
|
+
result[logicalId] = resolved.path;
|
|
67873
67986
|
}
|
|
67874
67987
|
return result;
|
|
67875
67988
|
}
|
|
@@ -67890,6 +68003,10 @@ async function loadStateOrEmpty(stackName, region, stateBackend) {
|
|
|
67890
68003
|
const unreadableProps = repairMalformedResourcePropertiesForReadOnly(result.state);
|
|
67891
68004
|
if (unreadableProps.length > 0) logger.warn(malformedResourcePropertiesWarning(stackName, region, unreadableProps));
|
|
67892
68005
|
if (repairMalformedOutputsForReadOnly(result.state)) logger.warn(malformedOutputsWarning(stackName, region));
|
|
68006
|
+
if (repairMalformedOrphansForReadOnly(result.state)) {
|
|
68007
|
+
logger.warn(malformedOrphansWarning(stackName, region));
|
|
68008
|
+
unreadable.push(UNREADABLE_ORPHANS_CONTAINER_ROW);
|
|
68009
|
+
}
|
|
67893
68010
|
if (isReadableBag(result.state.outputs) && !hasReadableExportSet(result.state)) logger.warn(malformedExportNamesWarning(stackName, region));
|
|
67894
68011
|
return {
|
|
67895
68012
|
state: result.state,
|
|
@@ -68666,10 +68783,10 @@ function renderDiffTree(node, isRoot, logFn) {
|
|
|
68666
68783
|
if (outputs.add + outputs.change + outputs.remove > 0) logFn(`${outputs.add} output(s) to add, ${outputs.change} to change, ${outputs.remove} to remove`);
|
|
68667
68784
|
if (node.adoptedOrphans.length > 0) logFn(`${node.adoptedOrphans.length} resource(s) to adopt from a previous rollback: ${node.adoptedOrphans.map(stripControlChars).join(", ")}`);
|
|
68668
68785
|
if (node.unreadable.length > 0) {
|
|
68669
|
-
const named = node.unreadable.slice(0, UNREADABLE_PREVIEW_NAMES).map((id) => id === "(resources map)" ? id : displayLogicalId(id));
|
|
68786
|
+
const named = node.unreadable.slice(0, UNREADABLE_PREVIEW_NAMES).map((id) => id === "(resources map)" || id === "(orphans container)" ? id : displayLogicalId(id));
|
|
68670
68787
|
const rest = node.unreadable.length - named.length;
|
|
68671
|
-
const
|
|
68672
|
-
logFn(`${node.unreadable.length} state record row(s) could not be read: ${named.join(", ")}${rest > 0 ? ` and ${rest} more` : ""}.` + (
|
|
68788
|
+
const onlyContainerRows = node.unreadable.every((id) => id === "(resources map)" || id === "(orphans container)");
|
|
68789
|
+
logFn(`${node.unreadable.length} state record row(s) could not be read: ${named.join(", ")}${rest > 0 ? ` and ${rest} more` : ""}.` + (onlyContainerRows ? "" : " One the template still declares is shown above as a create; one it no longer declares is not shown above."));
|
|
68673
68790
|
}
|
|
68674
68791
|
}
|
|
68675
68792
|
if (node.blocking.length > 0) {
|
|
@@ -68763,15 +68880,17 @@ async function diffCommand(stacks, options) {
|
|
|
68763
68880
|
...options.profile && { macroExpandS3ClientOpts: { profile: options.profile } },
|
|
68764
68881
|
deferMacroExpansion: true
|
|
68765
68882
|
};
|
|
68766
|
-
const
|
|
68883
|
+
const result = await synthesizer.synthesize(synthOptions);
|
|
68884
|
+
const { stacks: allStacks } = result;
|
|
68767
68885
|
logger.info(`Found ${allStacks.length} stack(s) in assembly`);
|
|
68768
68886
|
const stackPatterns = stacks.length > 0 ? stacks : options.stack ? [options.stack] : [];
|
|
68769
68887
|
let targetStacks;
|
|
68888
|
+
if (allStacks.length === 0) throw new Error(renderNoStackMatch(stackPatterns, allStacks, result));
|
|
68770
68889
|
if (options.all) targetStacks = allStacks;
|
|
68771
68890
|
else if (stackPatterns.length > 0) targetStacks = matchStacks(allStacks, stackPatterns);
|
|
68772
68891
|
else if (allStacks.length === 1) targetStacks = allStacks;
|
|
68773
68892
|
else throw new Error(`Multiple stacks found: ${allStacks.map(describeStack).join(", ")}. Specify stack name(s) or use --all`);
|
|
68774
|
-
if (targetStacks.length === 0) throw new Error(stackPatterns
|
|
68893
|
+
if (targetStacks.length === 0) throw new Error(renderNoStackMatch(stackPatterns, allStacks, result));
|
|
68775
68894
|
await synthesizer.expandMacrosForStacks(targetStacks, synthOptions);
|
|
68776
68895
|
const stateConfig = {
|
|
68777
68896
|
bucket: stateBucket,
|
|
@@ -68835,7 +68954,7 @@ async function diffCommand(stacks, options) {
|
|
|
68835
68954
|
displayName: stackInfo.stackName,
|
|
68836
68955
|
region: stackRegion,
|
|
68837
68956
|
template: stackInfo.template,
|
|
68838
|
-
nestedTemplates: stackInfo.nestedTemplates ??
|
|
68957
|
+
nestedTemplates: stackInfo.nestedTemplates ?? nullPrototypeRecord(),
|
|
68839
68958
|
recursive,
|
|
68840
68959
|
stateBackend,
|
|
68841
68960
|
diffCalculator,
|
|
@@ -69245,6 +69364,12 @@ async function scrubCommand(stacks, options) {
|
|
|
69245
69364
|
*/
|
|
69246
69365
|
const malformedOutputRecords = [];
|
|
69247
69366
|
/**
|
|
69367
|
+
* Stacks whose `orphans` CONTAINER could not be read (go-to-k/cdkd#3379).
|
|
69368
|
+
* Its own list for the reason `malformedOutputRecords` is its own: the
|
|
69369
|
+
* audited error names the container that is broken.
|
|
69370
|
+
*/
|
|
69371
|
+
const malformedOrphanRecords = [];
|
|
69372
|
+
/**
|
|
69248
69373
|
* Stacks whose scrub met a PRODUCER record with an unreadable `outputs` map
|
|
69249
69374
|
* (go-to-k/cdkd#3192 review round 5).
|
|
69250
69375
|
*
|
|
@@ -69332,6 +69457,7 @@ async function scrubCommand(stacks, options) {
|
|
|
69332
69457
|
} else if (scrubbed.secretBearingKeys === 0 && scrubbed.unverifiableReads === 0 && scrubbed.unverifiableLeaves === 0 && indexConverged === 0) logger.info(`No plaintext secrets found in ${stack.stackName}`);
|
|
69333
69458
|
if (scrubbed.malformedResources) malformedRecords.push(stack.stackName);
|
|
69334
69459
|
if (scrubbed.malformedOutputs) malformedOutputRecords.push(stack.stackName);
|
|
69460
|
+
if (scrubbed.malformedOrphans) malformedOrphanRecords.push(stack.stackName);
|
|
69335
69461
|
if (scrubbed.unverifiableLeaves > 0) {
|
|
69336
69462
|
totalStacksWithUnverifiableLeaves++;
|
|
69337
69463
|
logger.warn(`${scrubbed.unverifiableLeaves} scan(s) in ${stack.stackName} were ABANDONED mid-value because a {{resolve:...}} reference could not be resolved (see the warnings above). The resolver stops at the first failing token, so a real secret after it in the same value recorded no needle — this stack is not reported clean. The count is per abandoned SCAN, not per secret: one record can contribute more than once (an output whose Export.Name and Value both abandon), and one abandoned scan can hide several unexamined leaves.`);
|
|
@@ -69367,7 +69493,7 @@ async function scrubCommand(stacks, options) {
|
|
|
69367
69493
|
totalIndexEntriesUnexamined += unexamined.length;
|
|
69368
69494
|
logger.info(`Exports index (${indexRegion}): ${unexamined.length} entr${unexamined.length === 1 ? "y is" : "ies are"} published by a producer this run did not scrub, so nothing here read ${unexamined.length === 1 ? "its" : "their"} value. Coverage composes across the apps sharing this bucket and region — run each app's own cdkd scrub to reach its entries.`);
|
|
69369
69495
|
}
|
|
69370
|
-
if (totalStacksScrubbed === 0 && totalStacksWithUnscrubbableKeys === 0 && totalStacksWithUnverifiableReads === 0 && totalStacksWithUnverifiableLeaves === 0 && totalIndexEntriesConverged === 0 && indexUnwritten.length === 0 && indexUnreadable.length === 0 && malformedRecords.length === 0 && malformedOutputRecords.length === 0) {
|
|
69496
|
+
if (totalStacksScrubbed === 0 && totalStacksWithUnscrubbableKeys === 0 && totalStacksWithUnverifiableReads === 0 && totalStacksWithUnverifiableLeaves === 0 && totalIndexEntriesConverged === 0 && indexUnwritten.length === 0 && indexUnreadable.length === 0 && malformedRecords.length === 0 && malformedOutputRecords.length === 0 && malformedOrphanRecords.length === 0) {
|
|
69371
69497
|
if (failures.length === 0) {
|
|
69372
69498
|
logger.info("\nNo plaintext secrets found in any target stack state. Nothing to scrub.");
|
|
69373
69499
|
return;
|
|
@@ -69391,7 +69517,7 @@ async function scrubCommand(stacks, options) {
|
|
|
69391
69517
|
else logger.info(`\nPlan: no state record can be rewritten.${keyNote}${unverifiableNote}${leafNote}${indexNote}${failureNote} ROTATE any exposed secret.`);
|
|
69392
69518
|
if (failures.length > 0) throw scrubStacksFailedError(failures);
|
|
69393
69519
|
if (indexUnwritten.length > 0 || indexUnreadable.length > 0) throw exportIndexIncompleteError(indexUnwritten, indexUnreadable, true);
|
|
69394
|
-
if (malformedRecords.length > 0 || malformedOutputRecords.length > 0) throw malformedRecordsAuditedError(malformedRecords, malformedOutputRecords);
|
|
69520
|
+
if (malformedRecords.length > 0 || malformedOutputRecords.length > 0 || malformedOrphanRecords.length > 0) throw malformedRecordsAuditedError(malformedRecords, malformedOutputRecords, malformedOrphanRecords);
|
|
69395
69521
|
if (damagedProducerStacks.length > 0) throw damagedProducerRecordsError(damagedProducerStacks);
|
|
69396
69522
|
if (options.fail) throw new ScrubNeededError();
|
|
69397
69523
|
return;
|
|
@@ -69400,7 +69526,7 @@ async function scrubCommand(stacks, options) {
|
|
|
69400
69526
|
else logger.info(`\nNo state record was rewritten.${keyNote}${unverifiableNote}${leafNote}${indexNote}${failureNote} ROTATE any exposed secret.`);
|
|
69401
69527
|
if (failures.length > 0) throw scrubStacksFailedError(failures);
|
|
69402
69528
|
if (indexUnwritten.length > 0 || indexUnreadable.length > 0) throw exportIndexIncompleteError(indexUnwritten, indexUnreadable, false);
|
|
69403
|
-
if (malformedRecords.length > 0 || malformedOutputRecords.length > 0) throw malformedRecordsAuditedError(malformedRecords, malformedOutputRecords);
|
|
69529
|
+
if (malformedRecords.length > 0 || malformedOutputRecords.length > 0 || malformedOrphanRecords.length > 0) throw malformedRecordsAuditedError(malformedRecords, malformedOutputRecords, malformedOrphanRecords);
|
|
69404
69530
|
if (damagedProducerStacks.length > 0) throw damagedProducerRecordsError(damagedProducerStacks);
|
|
69405
69531
|
if (options.fail && (totalStacksWithUnscrubbableKeys > 0 || totalStacksWithUnverifiableReads > 0 || totalStacksWithUnverifiableLeaves > 0)) throw new ScrubNeededError();
|
|
69406
69532
|
}
|
|
@@ -70022,11 +70148,12 @@ function damagedProducerRecordsError(stackNames) {
|
|
|
70022
70148
|
const names = stackNames.map((n) => displayIdent(n)).join(", ");
|
|
70023
70149
|
return new ScrubRefusalError(`${stackNames.length} stack(s) import a value from a PRODUCER whose state record has no readable 'outputs' map, so this run could not tell whether that producer still holds the plaintext: ${names}. Those stacks were scrubbed for everything else and are NOT reported clean. Repair the producer record and scrub it first, then re-run. See the warnings above for which producer.`, "SCRUB_PRODUCER_RECORD_UNREADABLE");
|
|
70024
70150
|
}
|
|
70025
|
-
function malformedRecordsAuditedError(stackNames, outputStackNames) {
|
|
70151
|
+
function malformedRecordsAuditedError(stackNames, outputStackNames, orphanStackNames) {
|
|
70026
70152
|
const safeNames = (names) => names.map((n) => displayIdent(n)).join(", ");
|
|
70027
70153
|
const parts = [];
|
|
70028
70154
|
if (stackNames.length > 0) parts.push(`${stackNames.length} stack(s) were audited with an EMPTY resource set because their state record has no readable 'resources' map: ${safeNames(stackNames)}. The report above describes their outputs only — nothing is known about their resources, so this run cannot certify them clean.`);
|
|
70029
70155
|
if (outputStackNames.length > 0) parts.push(`${outputStackNames.length} stack(s) were audited with an EMPTY outputs bag because their state record has no readable 'outputs' map: ${safeNames(outputStackNames)}. The report above describes their resources only — nothing is known about the values their outputs hold, and those values are what the exports index republishes to consumer stacks, so this run cannot certify them clean.`);
|
|
70156
|
+
if (orphanStackNames.length > 0) parts.push(`${orphanStackNames.length} stack(s) were audited with an EMPTY orphan list because their state record has no readable 'orphans' list: ${safeNames(orphanStackNames)}. Nothing is known about resources an earlier failed deploy may have left live in AWS for them, so this run cannot certify them clean.`);
|
|
70030
70157
|
return new ScrubRefusalError(`${parts.join(" ")} See the warnings above for the record to inspect.`, STATE_RESOURCES_MALFORMED);
|
|
70031
70158
|
}
|
|
70032
70159
|
/**
|
|
@@ -71519,6 +71646,13 @@ async function scrubStack(stack, region, stateBackend, lockManager, opts) {
|
|
|
71519
71646
|
logger.warn(malformedOutputsWarning(stack.stackName, region));
|
|
71520
71647
|
}
|
|
71521
71648
|
} else if (!hasReadableOutputs(state)) throw new ScrubRefusalError(malformedOutputsRefusalMessage(stack.stackName, region), STATE_RESOURCES_MALFORMED);
|
|
71649
|
+
let malformedOrphans;
|
|
71650
|
+
if (opts.dryRun) {
|
|
71651
|
+
if (repairMalformedOrphansForReadOnly(state)) {
|
|
71652
|
+
malformedOrphans = true;
|
|
71653
|
+
logger.warn(malformedOrphansWarning(stack.stackName, region));
|
|
71654
|
+
}
|
|
71655
|
+
} else if (!hasReadableOrphans(state)) throw new ScrubRefusalError(malformedOrphansRefusalMessage(stack.stackName, region), STATE_RESOURCES_MALFORMED);
|
|
71522
71656
|
const perResourceTemplateProps = /* @__PURE__ */ new Map();
|
|
71523
71657
|
const outputsTemplateSource = {};
|
|
71524
71658
|
const resolvers = new ScrubResolvers(region);
|
|
@@ -71735,6 +71869,7 @@ async function scrubStack(stack, region, stateBackend, lockManager, opts) {
|
|
|
71735
71869
|
unverifiableLeaves,
|
|
71736
71870
|
...malformedResources ? { malformedResources } : {},
|
|
71737
71871
|
...malformedOutputs ? { malformedOutputs } : {},
|
|
71872
|
+
...malformedOrphans ? { malformedOrphans } : {},
|
|
71738
71873
|
outputs: state.outputs,
|
|
71739
71874
|
exportNameDisplay: (name) => secretSafeKeyDisplay(name, outputSecrets)
|
|
71740
71875
|
};
|
|
@@ -71813,6 +71948,7 @@ async function scrubStack(stack, region, stateBackend, lockManager, opts) {
|
|
|
71813
71948
|
unverifiableLeaves,
|
|
71814
71949
|
...malformedResources ? { malformedResources } : {},
|
|
71815
71950
|
...malformedOutputs ? { malformedOutputs } : {},
|
|
71951
|
+
...malformedOrphans ? { malformedOrphans } : {},
|
|
71816
71952
|
outputs: newOutputs,
|
|
71817
71953
|
exportNameDisplay: (name) => secretSafeKeyDisplay(name, outputSecrets)
|
|
71818
71954
|
};
|
|
@@ -71838,87 +71974,6 @@ function createScrubCommand() {
|
|
|
71838
71974
|
return cmd;
|
|
71839
71975
|
}
|
|
71840
71976
|
|
|
71841
|
-
//#endregion
|
|
71842
|
-
//#region src/utils/own-keys.ts
|
|
71843
|
-
/**
|
|
71844
|
-
* Own-key helpers for the bags that reach cdkd through `JSON.parse` — a
|
|
71845
|
-
* `state.json` record, a Cloud Control `Properties` document, a template — and
|
|
71846
|
-
* for the raw SDK readbacks compared against them.
|
|
71847
|
-
*
|
|
71848
|
-
* `JSON.parse` yields a key literally named `__proto__` as an ORDINARY own
|
|
71849
|
-
* data property, and every comparator / rebuild walk over such a bag has to
|
|
71850
|
-
* keep treating it as one. Two idioms silently do not:
|
|
71851
|
-
*
|
|
71852
|
-
* - **Rebuilding onto a `{}` literal.** `out[k] = v` with `k === '__proto__'`
|
|
71853
|
-
* runs `Object.prototype`'s accessor, SETS the node's prototype and DROPS
|
|
71854
|
-
* the key — on BOTH comparison sides when the walk is a canonicalizer, so a
|
|
71855
|
-
* drift at that key is invisible (issue
|
|
71856
|
-
* [#3121](https://github.com/go-to-k/cdkd/issues/3121)), and on the WRITE
|
|
71857
|
-
* path the member vanishes from the payload (issue
|
|
71858
|
-
* [#2899](https://github.com/go-to-k/cdkd/issues/2899)). Rebuild onto
|
|
71859
|
-
* {@link nullPrototypeRecord} (no accessor there, so plain assignment
|
|
71860
|
-
* defines an own key) or, onto an existing plain object, define the key
|
|
71861
|
-
* with {@link defineOwnKey}.
|
|
71862
|
-
* - **`key in bag` membership.** `in` reads the prototype chain, so a key
|
|
71863
|
-
* named `constructor` / `toString` answers `true` for every plain object
|
|
71864
|
-
* and the recursion then walks `Object`'s function. {@link hasOwnKey} asks
|
|
71865
|
-
* the bag alone; {@link ownValue} is the matching read.
|
|
71866
|
-
*
|
|
71867
|
-
* And ONE shape test every such walk needs FIRST: {@link hasPlainPrototype}.
|
|
71868
|
-
* A non-plain object (`Date`, `Uint8Array`, `Map`, a class instance) has no
|
|
71869
|
-
* own enumerable keys, so a key-walk over one FABRICATES `{}` (or an index
|
|
71870
|
-
* map) and any two of them compare equal. Such a value is returned by
|
|
71871
|
-
* IDENTITY and compared as itself.
|
|
71872
|
-
*
|
|
71873
|
-
* Extracted from `src/cli/commands/drift.ts` (issue #2899, swept by PR #3124)
|
|
71874
|
-
* so the analyzer-side canonicalizers (#3121) share ONE spelling rather than a
|
|
71875
|
-
* fourth copy. A LEAF module, deliberately, and it must stay one: a value
|
|
71876
|
-
* import from a module other suites `vi.mock` wholesale reds those suites
|
|
71877
|
-
* with a missing-export failure (which is why `secret-redaction.ts` keeps its
|
|
71878
|
-
* own `hasPlainPrototype` — several drift suites mock that module).
|
|
71879
|
-
*/
|
|
71880
|
-
/** Own-key membership: `in` reads the prototype chain, which a `JSON.parse`d bag never means. */
|
|
71881
|
-
function hasOwnKey(target, key) {
|
|
71882
|
-
return Object.prototype.hasOwnProperty.call(target, key);
|
|
71883
|
-
}
|
|
71884
|
-
/** An own-key read: `undefined` for an inherited name (`__proto__`, `constructor`, ...). */
|
|
71885
|
-
function ownValue(target, key) {
|
|
71886
|
-
return hasOwnKey(target, key) ? target[key] : void 0;
|
|
71887
|
-
}
|
|
71888
|
-
/** An ordinary (writable, enumerable, configurable) own data property — never the prototype. */
|
|
71889
|
-
function defineOwnKey(target, key, value) {
|
|
71890
|
-
Object.defineProperty(target, key, {
|
|
71891
|
-
value,
|
|
71892
|
-
writable: true,
|
|
71893
|
-
enumerable: true,
|
|
71894
|
-
configurable: true
|
|
71895
|
-
});
|
|
71896
|
-
}
|
|
71897
|
-
/**
|
|
71898
|
-
* Does this value carry an ORDINARY object prototype?
|
|
71899
|
-
*
|
|
71900
|
-
* `typeof v === 'object'` admits `Date` / `Map` / `Set` / `Uint8Array` / class
|
|
71901
|
-
* instances, whose own enumerable keys are `[]` — so a key-walk over one
|
|
71902
|
-
* reports "no contradiction" between two values that actually differ, and a
|
|
71903
|
-
* rebuild over one fabricates `{}`. A NULL prototype counts as plain: it is
|
|
71904
|
-
* what {@link nullPrototypeRecord} yields, and a walk must be able to re-walk
|
|
71905
|
-
* its own output.
|
|
71906
|
-
*/
|
|
71907
|
-
function hasPlainPrototype(value) {
|
|
71908
|
-
const proto = Object.getPrototypeOf(value);
|
|
71909
|
-
return proto === Object.prototype || proto === null;
|
|
71910
|
-
}
|
|
71911
|
-
/**
|
|
71912
|
-
* A fresh rebuild target with NO prototype, so `out['__proto__'] = v` defines
|
|
71913
|
-
* an own key instead of setting the prototype. `JSON.stringify`,
|
|
71914
|
-
* `Object.keys` / `entries` and `structuredClone` all read it like a plain
|
|
71915
|
-
* object; only `instanceof Object` and a direct `.hasOwnProperty()` method call
|
|
71916
|
-
* do not, and no consumer on the drift path uses either.
|
|
71917
|
-
*/
|
|
71918
|
-
function nullPrototypeRecord() {
|
|
71919
|
-
return Object.create(null);
|
|
71920
|
-
}
|
|
71921
|
-
|
|
71922
71977
|
//#endregion
|
|
71923
71978
|
//#region src/analyzer/drift-normalize.ts
|
|
71924
71979
|
/**
|
|
@@ -78112,7 +78167,7 @@ async function exportCommand(stackArg, options) {
|
|
|
78112
78167
|
let synthedRegion;
|
|
78113
78168
|
let templateFormat = "json";
|
|
78114
78169
|
let allSynthStacks = [];
|
|
78115
|
-
let rootNestedTemplatePaths =
|
|
78170
|
+
let rootNestedTemplatePaths = nullPrototypeRecord();
|
|
78116
78171
|
if (options.template) {
|
|
78117
78172
|
if (!stackArg) throw new Error("--template requires a stack name as a positional argument to identify the cdkd state record.");
|
|
78118
78173
|
const parsed = parseTemplateFile(options.template);
|
|
@@ -78141,7 +78196,7 @@ async function exportCommand(stackArg, options) {
|
|
|
78141
78196
|
template = stackInfo.template;
|
|
78142
78197
|
resolvedStackName = stackInfo.stackName;
|
|
78143
78198
|
synthedRegion = stackInfo.region;
|
|
78144
|
-
rootNestedTemplatePaths = stackInfo.nestedTemplates ??
|
|
78199
|
+
rootNestedTemplatePaths = stackInfo.nestedTemplates ?? nullPrototypeRecord();
|
|
78145
78200
|
allSynthStacks = result.stacks.map((s) => ({
|
|
78146
78201
|
stackName: s.stackName,
|
|
78147
78202
|
template: s.template
|
|
@@ -78717,11 +78772,16 @@ function readNestedChildTemplateFile(templatePath, childLogicalId) {
|
|
|
78717
78772
|
* non-CDK toolchain — `path.join(dir, '/abs/foo')` would silently bypass
|
|
78718
78773
|
* the `dir` argument).
|
|
78719
78774
|
*
|
|
78775
|
+
* Also refuses a path that RESOLVES outside `templateDir`, which the absolute
|
|
78776
|
+
* check cannot see — `path.join` folds `..`
|
|
78777
|
+
* ([#3489](https://github.com/go-to-k/cdkd/issues/3489)).
|
|
78778
|
+
*
|
|
78720
78779
|
* Sibling of `indexGrandchildTemplatePaths` in `import.ts`. Same rationale
|
|
78721
|
-
* for separate definitions as `readNestedChildTemplateFile` above.
|
|
78780
|
+
* for separate definitions as `readNestedChildTemplateFile` above. Exported
|
|
78781
|
+
* for unit testing.
|
|
78722
78782
|
*/
|
|
78723
78783
|
function indexNestedTemplatePaths(template, templateDir) {
|
|
78724
|
-
const result =
|
|
78784
|
+
const result = nullPrototypeRecord();
|
|
78725
78785
|
const resources = template["Resources"];
|
|
78726
78786
|
if (!resources || typeof resources !== "object" || Array.isArray(resources)) return result;
|
|
78727
78787
|
for (const [logicalId, resource] of Object.entries(resources)) {
|
|
@@ -78730,8 +78790,10 @@ function indexNestedTemplatePaths(template, templateDir) {
|
|
|
78730
78790
|
if (r.Type !== "AWS::CloudFormation::Stack") continue;
|
|
78731
78791
|
const assetPath = r.Metadata?.["aws:asset:path"];
|
|
78732
78792
|
if (typeof assetPath !== "string" || assetPath.length === 0) continue;
|
|
78733
|
-
if (path.isAbsolute(assetPath)) throw new Error(`cdkd export: nested-stack '${logicalId}' has Metadata['aws:asset:path']='${assetPath}' which is absolute. CDK emits relative asset paths for nested templates.`);
|
|
78734
|
-
|
|
78793
|
+
if (path.isAbsolute(assetPath)) throw new Error(`cdkd export: nested-stack '${displaySafe(logicalId)}' has Metadata['aws:asset:path']='${displaySafe(assetPath)}' which is absolute. CDK emits relative asset paths for nested templates.`);
|
|
78794
|
+
const resolved = resolveAssemblyPath(templateDir, assetPath);
|
|
78795
|
+
if (!resolved.contained) throw new Error(`cdkd export: nested-stack '${displaySafe(logicalId)}' has Metadata['aws:asset:path']='${displaySafe(assetPath)}' which ${renderAssemblyPathEscape(resolved, templateDir)}`);
|
|
78796
|
+
result[logicalId] = resolved.path;
|
|
78735
78797
|
}
|
|
78736
78798
|
return result;
|
|
78737
78799
|
}
|
|
@@ -82987,6 +83049,7 @@ async function rollbackCommand(stackArg, options) {
|
|
|
82987
83049
|
if (!stateData) throw new Error(`Rollback journal exists for '${safeStack(stackName)}' (${safe(region)}) but its state.json is missing (keys: ${safe(setup.prefix)}/${safeStack(stackName)}/${safe(region)}/state.json and .../rollback-journal.json). State appears corrupted — inspect the bucket manually.`);
|
|
82988
83050
|
const baseState = stateData.state;
|
|
82989
83051
|
refuseMalformedState(baseState, stackName, region);
|
|
83052
|
+
refuseMalformedOrphans(baseState, stackName, region);
|
|
82990
83053
|
const stateResources = { ...baseState.resources };
|
|
82991
83054
|
const mintedOrphans = [];
|
|
82992
83055
|
const orphanLogicalIds = new Set(options.orphan ?? []);
|
|
@@ -85692,15 +85755,17 @@ async function publishAssetsCommand(stacks, options) {
|
|
|
85692
85755
|
...options.profile && { profile: options.profile },
|
|
85693
85756
|
...Object.keys(context).length > 0 && { context }
|
|
85694
85757
|
};
|
|
85695
|
-
const
|
|
85758
|
+
const result = await synthesizer.synthesize(synthOptions);
|
|
85759
|
+
const { stacks: allStacks } = result;
|
|
85696
85760
|
logger.debug(`Found ${allStacks.length} stack(s) in assembly`);
|
|
85697
85761
|
const stackPatterns = stacks.length > 0 ? stacks : options.stack ? [options.stack] : [];
|
|
85698
85762
|
let targetStacks;
|
|
85763
|
+
if (allStacks.length === 0) throw new Error(renderNoStackMatch(stackPatterns, allStacks, result));
|
|
85699
85764
|
if (options.all) targetStacks = allStacks;
|
|
85700
85765
|
else if (stackPatterns.length > 0) targetStacks = matchStacks(allStacks, stackPatterns);
|
|
85701
85766
|
else if (allStacks.length === 1) targetStacks = allStacks;
|
|
85702
85767
|
else throw new Error(`Multiple stacks found: ${allStacks.map(describeStack).join(", ")}. Specify stack name(s) or use --all`);
|
|
85703
|
-
if (targetStacks.length === 0) throw new Error(stackPatterns
|
|
85768
|
+
if (targetStacks.length === 0) throw new Error(renderNoStackMatch(stackPatterns, allStacks, result));
|
|
85704
85769
|
const baseRegion = namedCliRegion(options.region) ?? "us-east-1";
|
|
85705
85770
|
const { STSClient, GetCallerIdentityCommand } = await import("@aws-sdk/client-sts");
|
|
85706
85771
|
const stsClient = new STSClient({
|
|
@@ -85781,7 +85846,8 @@ async function publishAssetsCommand(stacks, options) {
|
|
|
85781
85846
|
region: stack.region || baseRegion,
|
|
85782
85847
|
...options.profile && { profile: options.profile },
|
|
85783
85848
|
nodePrefix: `${stack.stackName}:`,
|
|
85784
|
-
...redirect && { redirect }
|
|
85849
|
+
...redirect && { redirect },
|
|
85850
|
+
...stack.assetOutdir !== void 0 && { assetOutdir: stack.assetOutdir }
|
|
85785
85851
|
});
|
|
85786
85852
|
} catch (err) {
|
|
85787
85853
|
if (err.code === "ENOENT") {
|
|
@@ -86201,7 +86267,7 @@ async function importCommand(stackArg, options) {
|
|
|
86201
86267
|
overrides
|
|
86202
86268
|
});
|
|
86203
86269
|
logger.info(`Resolved ${mergeStats.derived} physical ID(s) from CloudFormation` + formatCfnOverrideMergeDetail(mergeStats));
|
|
86204
|
-
validateNestedStackShape(template, migrationTree, stackInfo.stackName, stackInfo.nestedTemplates ??
|
|
86270
|
+
validateNestedStackShape(template, migrationTree, stackInfo.stackName, stackInfo.nestedTemplates ?? nullPrototypeRecord());
|
|
86205
86271
|
}
|
|
86206
86272
|
let accountIdForNestedSynth;
|
|
86207
86273
|
if (migrationTree && migrationTree.nested.size > 0) {
|
|
@@ -86229,6 +86295,7 @@ async function importCommand(stackArg, options) {
|
|
|
86229
86295
|
const existingState = existingResult?.state ?? null;
|
|
86230
86296
|
if (existingState) refuseMalformedState(existingState, stackInfo.stackName, targetRegion);
|
|
86231
86297
|
if (existingState) refuseMalformedOutputs(existingState, stackInfo.stackName, targetRegion);
|
|
86298
|
+
if (existingState) refuseMalformedOrphans(existingState, stackInfo.stackName, targetRegion);
|
|
86232
86299
|
const existingEtag = existingResult?.etag;
|
|
86233
86300
|
const migrationPending = existingResult?.migrationPending ?? false;
|
|
86234
86301
|
if (existingState) {
|
|
@@ -86319,7 +86386,7 @@ async function importCommand(stackArg, options) {
|
|
|
86319
86386
|
lockRecovery,
|
|
86320
86387
|
parentStackName: stackInfo.stackName,
|
|
86321
86388
|
parentRegion: targetRegion,
|
|
86322
|
-
parentNestedTemplates: stackInfo.nestedTemplates ??
|
|
86389
|
+
parentNestedTemplates: stackInfo.nestedTemplates ?? nullPrototypeRecord(),
|
|
86323
86390
|
parentTree: migrationTree,
|
|
86324
86391
|
stateBackend,
|
|
86325
86392
|
lockManager,
|
|
@@ -87597,16 +87664,23 @@ function readNestedChildTemplate(templatePath, childLogicalId) {
|
|
|
87597
87664
|
* does: an absolute path indicates the synth output was hand-modified or
|
|
87598
87665
|
* generated by a non-CDK toolchain — `path.join(dir, '/abs/foo')` would
|
|
87599
87666
|
* silently bypass our `dir` resolution and point outside cdk.out.
|
|
87667
|
+
*
|
|
87668
|
+
* Also refuses a path that RESOLVES outside `dir`, which the absolute check
|
|
87669
|
+
* cannot see — `path.join` folds `..`
|
|
87670
|
+
* ([#3489](https://github.com/go-to-k/cdkd/issues/3489)). Exported for unit
|
|
87671
|
+
* testing.
|
|
87600
87672
|
*/
|
|
87601
87673
|
function indexGrandchildTemplatePaths(childTemplate, childTemplatePath) {
|
|
87602
87674
|
const dir = path.dirname(childTemplatePath);
|
|
87603
|
-
const result =
|
|
87675
|
+
const result = nullPrototypeRecord();
|
|
87604
87676
|
for (const [grandLogicalId, resource] of Object.entries(childTemplate.Resources)) {
|
|
87605
87677
|
if (resource.Type !== "AWS::CloudFormation::Stack") continue;
|
|
87606
87678
|
const assetPath = resource.Metadata?.["aws:asset:path"];
|
|
87607
87679
|
if (typeof assetPath !== "string" || assetPath.length === 0) continue;
|
|
87608
|
-
if (path.isAbsolute(assetPath)) throw new Error(`cdkd import --migrate-from-cloudformation: grandchild nested-stack '${grandLogicalId}' has Metadata['aws:asset:path']='${assetPath}' which is absolute. CDK emits relative asset paths for nested templates.`);
|
|
87609
|
-
|
|
87680
|
+
if (path.isAbsolute(assetPath)) throw new Error(`cdkd import --migrate-from-cloudformation: grandchild nested-stack '${displaySafe(grandLogicalId)}' has Metadata['aws:asset:path']='${displaySafe(assetPath)}' which is absolute. CDK emits relative asset paths for nested templates.`);
|
|
87681
|
+
const resolved = resolveAssemblyPath(dir, assetPath);
|
|
87682
|
+
if (!resolved.contained) throw new Error(`cdkd import --migrate-from-cloudformation: grandchild nested-stack '${displaySafe(grandLogicalId)}' has Metadata['aws:asset:path']='${displaySafe(assetPath)}' which ${renderAssemblyPathEscape(resolved, dir)}`);
|
|
87683
|
+
result[grandLogicalId] = resolved.path;
|
|
87610
87684
|
}
|
|
87611
87685
|
return result;
|
|
87612
87686
|
}
|
|
@@ -88825,7 +88899,7 @@ function notFoundError$1(target, stack, resources) {
|
|
|
88825
88899
|
*/
|
|
88826
88900
|
function copyLayerTreeLastWins(src, dest) {
|
|
88827
88901
|
mkdirSync(dest, { recursive: true });
|
|
88828
|
-
copyDirLastWins(realpathSync(src), dest);
|
|
88902
|
+
copyDirLastWins(realpathSync.native(src), dest);
|
|
88829
88903
|
}
|
|
88830
88904
|
function copyDirLastWins(fromDir, toDir) {
|
|
88831
88905
|
for (const entry of readdirSync(fromDir, { withFileTypes: true })) {
|
|
@@ -92591,7 +92665,7 @@ function materializeInlineCode$1(handler, source, fileExtension, tmpDirsOut) {
|
|
|
92591
92665
|
const modulePath = handler.substring(0, lastDot);
|
|
92592
92666
|
const dir = mkdtempSync(path.join(tmpdir(), "cdkd-local-start-api-"));
|
|
92593
92667
|
tmpDirsOut.add(dir);
|
|
92594
|
-
const filePath =
|
|
92668
|
+
const filePath = resolveInlineCodeFilePath(dir, modulePath, fileExtension, handler);
|
|
92595
92669
|
mkdirSync(path.dirname(filePath), { recursive: true });
|
|
92596
92670
|
writeFileSync(filePath, source, "utf-8");
|
|
92597
92671
|
return dir;
|
|
@@ -93781,8 +93855,9 @@ async function prepareOneImage(task, container, options) {
|
|
|
93781
93855
|
const tag = `cdkd-local-run-task-${(image.assetHash ?? "single").slice(0, 16)}`;
|
|
93782
93856
|
const actualTag = await buildDockerImage(asset, cdkOutDir, {
|
|
93783
93857
|
tag,
|
|
93858
|
+
...task.stack.assetOutdir !== void 0 && { assetOutdir: task.stack.assetOutdir },
|
|
93784
93859
|
...options.platformOverride !== void 0 && { platform: options.platformOverride },
|
|
93785
|
-
wrapError: (stderr) => new LocalInvokeBuildError$1(`docker build failed for ECS container '${container.name}' (${asset.source.directory
|
|
93860
|
+
wrapError: (stderr) => new LocalInvokeBuildError$1(`docker build failed for ECS container '${displaySafe(container.name)}' (${asset.source.directory !== void 0 ? displaySafe(asset.source.directory) : asset.source.executable ? redactDockerArgvValues(asset.source.executable).join(" ") : void 0}): ${displaySafe(stderr)}`)
|
|
93786
93861
|
});
|
|
93787
93862
|
if (actualTag !== tag) {
|
|
93788
93863
|
const tagArgs = [
|
|
@@ -93793,7 +93868,7 @@ async function prepareOneImage(task, container, options) {
|
|
|
93793
93868
|
try {
|
|
93794
93869
|
await runDockerStreaming(tagArgs);
|
|
93795
93870
|
} catch (err) {
|
|
93796
|
-
throw new LocalInvokeBuildError$1(`docker tag failed re-tagging '${actualTag}' → '${tag}' for ECS container '${container.name}': ${describeDockerFailure(err, tagArgs)}`);
|
|
93871
|
+
throw new LocalInvokeBuildError$1(`docker tag failed re-tagging '${displaySafe(actualTag)}' → '${displaySafe(tag)}' for ECS container '${displaySafe(container.name)}': ${displaySafe(describeDockerFailure(err, tagArgs))}`);
|
|
93797
93872
|
}
|
|
93798
93873
|
}
|
|
93799
93874
|
return tag;
|
|
@@ -94711,13 +94786,16 @@ async function loadAgentCoreAssetContext(args) {
|
|
|
94711
94786
|
const { resolvedTarget, resolved, stacks, cdkOutDir, assetLoader, oldAssetHash } = args;
|
|
94712
94787
|
const newCandidate = pickAgentCoreCandidateStack(resolvedTarget, stacks);
|
|
94713
94788
|
if (!newCandidate) return void 0;
|
|
94789
|
+
const cdkdRecord = stacks.find((s) => s.stackName === newCandidate.stackName);
|
|
94790
|
+
const manifestDir = cdkdRecord?.assetManifestPath ? dirname(cdkdRecord.assetManifestPath) : cdkOutDir;
|
|
94791
|
+
const assetOutdir = cdkdRecord?.assetOutdir ?? manifestDir;
|
|
94714
94792
|
if (resolved.codeArtifact) {
|
|
94715
94793
|
if (resolved.codeArtifact.s3Source) return void 0;
|
|
94716
|
-
const manifest = await assetLoader.loadManifest(
|
|
94794
|
+
const manifest = await assetLoader.loadManifest(manifestDir, newCandidate.stackName);
|
|
94717
94795
|
if (!manifest) return void 0;
|
|
94718
94796
|
const asset = assetLoader.getFileAssets(manifest).get(resolved.codeArtifact.codeAssetHash);
|
|
94719
94797
|
if (!asset) return void 0;
|
|
94720
|
-
const sourceDir = assetLoader.getAssetSourcePath(
|
|
94798
|
+
const sourceDir = assetLoader.getAssetSourcePath(manifestDir, asset, assetOutdir);
|
|
94721
94799
|
return {
|
|
94722
94800
|
...oldAssetHash !== void 0 && { oldAssetHash },
|
|
94723
94801
|
newAssetHash: resolved.codeArtifact.codeAssetHash,
|
|
@@ -94726,13 +94804,13 @@ async function loadAgentCoreAssetContext(args) {
|
|
|
94726
94804
|
};
|
|
94727
94805
|
}
|
|
94728
94806
|
if (resolved.containerUri === void 0) return void 0;
|
|
94729
|
-
const manifest = await assetLoader.loadManifest(
|
|
94807
|
+
const manifest = await assetLoader.loadManifest(manifestDir, newCandidate.stackName);
|
|
94730
94808
|
if (!manifest) return void 0;
|
|
94731
94809
|
const dockerImageEntry = getDockerImageBySourceHash(manifest, resolved.containerUri);
|
|
94732
94810
|
if (!dockerImageEntry) return void 0;
|
|
94733
94811
|
const newDockerImage = dockerImageEntry.asset;
|
|
94734
94812
|
if (!newDockerImage.source.directory) return void 0;
|
|
94735
|
-
const newAssetSourceDir =
|
|
94813
|
+
const newAssetSourceDir = resolveDockerContextDirectory(manifestDir, newDockerImage.source.directory, (message) => new Error(message), assetOutdir);
|
|
94736
94814
|
return {
|
|
94737
94815
|
...oldAssetHash !== void 0 && { oldAssetHash },
|
|
94738
94816
|
newAssetHash: dockerImageEntry.hash,
|
|
@@ -94749,6 +94827,9 @@ async function loadAgentCoreAssetContext(args) {
|
|
|
94749
94827
|
* "force rebuild", which is the conservative default.
|
|
94750
94828
|
*
|
|
94751
94829
|
* NOT exported from `cdk-local/internal`, so copied locally.
|
|
94830
|
+
*
|
|
94831
|
+
* @internal — exported so its manifest lookup can be fenced beside
|
|
94832
|
+
* {@link loadAgentCoreAssetContext}'s, which is its twin.
|
|
94752
94833
|
*/
|
|
94753
94834
|
async function deriveOldAssetHash(args) {
|
|
94754
94835
|
const { resolvedTarget, resolved, stacks, cdkOutDir, assetLoader } = args;
|
|
@@ -94756,7 +94837,9 @@ async function deriveOldAssetHash(args) {
|
|
|
94756
94837
|
if (resolved.containerUri === void 0) return void 0;
|
|
94757
94838
|
const candidate = pickAgentCoreCandidateStack(resolvedTarget, stacks);
|
|
94758
94839
|
if (!candidate) return void 0;
|
|
94759
|
-
const
|
|
94840
|
+
const manifestFile = stacks.find((s) => s.stackName === candidate.stackName)?.assetManifestPath;
|
|
94841
|
+
const manifestDir = manifestFile !== void 0 ? dirname(manifestFile) : cdkOutDir;
|
|
94842
|
+
const manifest = await assetLoader.loadManifest(manifestDir, candidate.stackName);
|
|
94760
94843
|
if (!manifest) return void 0;
|
|
94761
94844
|
return getDockerImageBySourceHash(manifest, resolved.containerUri)?.hash;
|
|
94762
94845
|
}
|
|
@@ -94841,7 +94924,7 @@ async function localInvokeAgentCoreCommand(target, options) {
|
|
|
94841
94924
|
...options.profile && { profile: options.profile },
|
|
94842
94925
|
...Object.keys(context).length > 0 && { context }
|
|
94843
94926
|
};
|
|
94844
|
-
const { stacks } = await synthesizer.synthesize(synthOpts);
|
|
94927
|
+
const { stacks, assemblyDir } = await synthesizer.synthesize(synthOpts);
|
|
94845
94928
|
const resolvedTarget = await resolveSingleTarget(target, {
|
|
94846
94929
|
entries: listTargets(stacks).agentCoreRuntimes,
|
|
94847
94930
|
message: "Select an AgentCore Runtime to invoke",
|
|
@@ -94879,6 +94962,7 @@ async function localInvokeAgentCoreCommand(target, options) {
|
|
|
94879
94962
|
const boot = await bootAgentCoreContainer({
|
|
94880
94963
|
resolved,
|
|
94881
94964
|
options,
|
|
94965
|
+
assemblyDir,
|
|
94882
94966
|
profileCredentials,
|
|
94883
94967
|
profileCredsFile,
|
|
94884
94968
|
stateProvider,
|
|
@@ -95089,9 +95173,9 @@ function isAgentCoreWatchEligible(protocol) {
|
|
|
95089
95173
|
*/
|
|
95090
95174
|
async function bootAgentCoreContainer(args) {
|
|
95091
95175
|
const logger = getLogger();
|
|
95092
|
-
const { resolved, options, profileCredentials, profileCredsFile, stateProvider, loadedState, imageContext, isMcp, isA2a } = args;
|
|
95176
|
+
const { resolved, options, assemblyDir, profileCredentials, profileCredsFile, stateProvider, loadedState, imageContext, isMcp, isA2a } = args;
|
|
95093
95177
|
await resolveFromS3BucketIntrinsic(resolved, stateProvider, loadedState, imageContext);
|
|
95094
|
-
const image = await resolveAgentCoreImage(resolved, options, loadedState);
|
|
95178
|
+
const image = await resolveAgentCoreImage(resolved, options, assemblyDir, loadedState);
|
|
95095
95179
|
const { env: dockerEnv, sensitiveEnvKeys } = await buildContainerEnv(resolved, options, profileCredentials, profileCredsFile, stateProvider, loadedState, imageContext);
|
|
95096
95180
|
const hostPort = await pickFreePort();
|
|
95097
95181
|
const containerHost = options.containerHost;
|
|
@@ -95138,7 +95222,7 @@ async function bootAgentCoreContainer(args) {
|
|
|
95138
95222
|
async function rebuildAgentCoreContainer(args) {
|
|
95139
95223
|
const { cleanupBefore, resolvedTarget, options, synthesizer, synthOpts, profileCredentials, profileCredsFile, stateProvider, isMcp, isA2a } = args;
|
|
95140
95224
|
await cleanupBefore();
|
|
95141
|
-
const { stacks: newStacks } = await synthesizer.synthesize(synthOpts);
|
|
95225
|
+
const { stacks: newStacks, assemblyDir: newAssemblyDir } = await synthesizer.synthesize(synthOpts);
|
|
95142
95226
|
const newCandidate = pickAgentCoreCandidateStack$1(resolvedTarget, newStacks);
|
|
95143
95227
|
const { context: newImageContext, loaded: newLoaded } = stateProvider && newCandidate ? await buildAgentCoreImageContext(newCandidate, stateProvider, options) : {
|
|
95144
95228
|
context: void 0,
|
|
@@ -95148,6 +95232,7 @@ async function rebuildAgentCoreContainer(args) {
|
|
|
95148
95232
|
...await bootAgentCoreContainer({
|
|
95149
95233
|
resolved: resolveAgentCoreTarget(resolvedTarget, newStacks, newImageContext),
|
|
95150
95234
|
options,
|
|
95235
|
+
assemblyDir: newAssemblyDir,
|
|
95151
95236
|
profileCredentials,
|
|
95152
95237
|
profileCredsFile,
|
|
95153
95238
|
stateProvider,
|
|
@@ -95328,11 +95413,18 @@ async function resolveHostCredentialsForSigV4(options, resolved, loaded, region)
|
|
|
95328
95413
|
* `loaded` is the `--from-cfn-stack` state record (when available) — threaded
|
|
95329
95414
|
* through so a bare `--assume-role` can resolve the execution-role ARN from
|
|
95330
95415
|
* state for the fromS3 download.
|
|
95416
|
+
*
|
|
95417
|
+
* `assemblyDir` is the ASSEMBLY ROOT as `Synthesizer.synthesize` reported it,
|
|
95418
|
+
* and is the containment bound for an asset path read out of the manifest
|
|
95419
|
+
* (issue go-to-k/cdkd#3489). It is NOT `options.output`: under
|
|
95420
|
+
* `-a <pre-synthesized dir>` synthesis is skipped and the assembly root is
|
|
95421
|
+
* `resolve(options.app)` while `--output` keeps its `cdk.out` default, so
|
|
95422
|
+
* binding to `--output` would refuse every asset of an untouched assembly.
|
|
95331
95423
|
*/
|
|
95332
|
-
async function resolveAgentCoreImage(resolved, options, loaded) {
|
|
95424
|
+
async function resolveAgentCoreImage(resolved, options, assemblyDir, loaded) {
|
|
95333
95425
|
const logger = getLogger();
|
|
95334
95426
|
const architecture = platformToArchitecture(options.platform);
|
|
95335
|
-
if (resolved.codeArtifact) return resolveAgentCoreCodeImage(resolved, resolved.codeArtifact, options, architecture, loaded);
|
|
95427
|
+
if (resolved.codeArtifact) return resolveAgentCoreCodeImage(resolved, resolved.codeArtifact, options, architecture, assemblyDir, loaded);
|
|
95336
95428
|
const containerUri = resolved.containerUri;
|
|
95337
95429
|
if (containerUri === void 0) throw new CdkdError(`AgentCore Runtime '${resolved.logicalId}' has neither a container image nor a code artifact to run.`, "LOCAL_INVOKE_AGENTCORE_NO_ARTIFACT");
|
|
95338
95430
|
const manifestPath = resolved.stack.assetManifestPath;
|
|
@@ -95368,7 +95460,7 @@ async function resolveAgentCoreImage(resolved, options, loaded) {
|
|
|
95368
95460
|
* run the same from-source build (generated Dockerfile → install deps → run
|
|
95369
95461
|
* EntryPoint).
|
|
95370
95462
|
*/
|
|
95371
|
-
async function resolveAgentCoreCodeImage(resolved, code, options, architecture, loaded) {
|
|
95463
|
+
async function resolveAgentCoreCodeImage(resolved, code, options, architecture, assemblyDir, loaded) {
|
|
95372
95464
|
if (code.s3Source) return resolveAgentCoreCodeImageFromS3(resolved, code, code.s3Source, options, architecture, loaded);
|
|
95373
95465
|
const manifestPath = resolved.stack.assetManifestPath;
|
|
95374
95466
|
if (!manifestPath) throw new CdkdError(`AgentCore Runtime '${resolved.logicalId}' uses a code artifact, but its stack has no asset manifest in cdk.out to read the bundle source from.`, "LOCAL_INVOKE_AGENTCORE_CODE_NO_MANIFEST");
|
|
@@ -95378,8 +95470,8 @@ async function resolveAgentCoreCodeImage(resolved, code, options, architecture,
|
|
|
95378
95470
|
const fileAssets = manifest ? loader.getFileAssets(manifest) : void 0;
|
|
95379
95471
|
const asset = fileAssets ? fileAssets.get(code.codeAssetHash) ?? findFileAssetByObjectKey(fileAssets, code.codeAssetHash) : void 0;
|
|
95380
95472
|
if (!asset) throw new CdkdError(`AgentCore Runtime '${resolved.logicalId}' code bundle (asset ${code.codeAssetHash}) was not found in the cdk.out asset manifest. ${getEmbedConfig().cliName} invoke-agentcore runs a local from-source build of a fromCodeAsset bundle — re-synthesize the app so the asset is staged in cdk.out and retry. (A fromS3 bundle is downloaded from S3 instead; this runtime has no literal Code.S3.Bucket.)`, "LOCAL_INVOKE_AGENTCORE_CODE_ASSET_NOT_FOUND");
|
|
95381
|
-
const sourceDir = loader.getAssetSourcePath(cdkOutDir, asset);
|
|
95382
|
-
if (!existsSync(sourceDir) || !statSync(sourceDir).isDirectory()) throw new CdkdError(`AgentCore Runtime '${resolved.logicalId}' code bundle source '${sourceDir}' does not exist or is not a directory. Re-synthesize the app and retry.`, "LOCAL_INVOKE_AGENTCORE_CODE_SOURCE_MISSING");
|
|
95473
|
+
const sourceDir = loader.getAssetSourcePath(cdkOutDir, asset, assemblyDir);
|
|
95474
|
+
if (!existsSync(sourceDir) || !statSync(sourceDir).isDirectory()) throw new CdkdError(`AgentCore Runtime '${displaySafe(resolved.logicalId)}' code bundle source '${displaySafe(sourceDir)}' does not exist or is not a directory. Re-synthesize the app and retry.`, "LOCAL_INVOKE_AGENTCORE_CODE_SOURCE_MISSING");
|
|
95383
95475
|
return buildAgentCoreCodeImage({
|
|
95384
95476
|
sourceDir,
|
|
95385
95477
|
runtime: code.runtime,
|
|
@@ -97037,6 +97129,31 @@ function applyProfileCredentialsOverlay(env, profileCreds, assumeRoleActive) {
|
|
|
97037
97129
|
else delete env["AWS_SESSION_TOKEN"];
|
|
97038
97130
|
}
|
|
97039
97131
|
/**
|
|
97132
|
+
* Where an inline `Code.ZipFile` body is written inside `dir`, refusing a
|
|
97133
|
+
* `Handler` whose module path escapes it (issue
|
|
97134
|
+
* [#3489](https://github.com/go-to-k/cdkd/issues/3489)).
|
|
97135
|
+
*
|
|
97136
|
+
* `Handler` is template-supplied, and `materializeInlineCode` turned it into a
|
|
97137
|
+
* FILENAME and then `mkdirSync(dirname, { recursive: true })` + `writeFileSync`
|
|
97138
|
+
* — so `Handler: '../../victim/evil.handler'` wrote attacker-chosen JavaScript
|
|
97139
|
+
* to an arbitrary host path. Measured: the file was created outside the temp
|
|
97140
|
+
* directory. That is a write, not the "we run their handler in a container"
|
|
97141
|
+
* trade cdkd already accepts.
|
|
97142
|
+
*
|
|
97143
|
+
* `dir` is a temp directory cdkd just created rather than the assembly, but
|
|
97144
|
+
* the containment question is identical and `resolveAssemblyPath` answers it
|
|
97145
|
+
* unchanged: `mkdtemp` sits under `/var/folders/...`, reached through macOS's
|
|
97146
|
+
* `/var -> /private/var` link, and the helper realpaths BOTH sides so that is
|
|
97147
|
+
* a no-op. Only the refusal's provenance differs, so it is passed explicitly.
|
|
97148
|
+
*
|
|
97149
|
+
* Exported for unit testing.
|
|
97150
|
+
*/
|
|
97151
|
+
function resolveInlineCodeFilePath(dir, modulePath, fileExtension, handler) {
|
|
97152
|
+
const resolved = resolveAssemblyPath(dir, `${modulePath}${fileExtension}`);
|
|
97153
|
+
if (!resolved.contained) throw new Error(`Handler '${displaySafe(handler)}' names a module path that ${renderAssemblyPathEscape(resolved, dir, "materialize it", "A Handler's module path names a file inside the function's own code directory; one that leaves it would write the inline Code.ZipFile body to an arbitrary path on this machine. Refusing to materialize it.")}`);
|
|
97154
|
+
return resolved.path;
|
|
97155
|
+
}
|
|
97156
|
+
/**
|
|
97040
97157
|
* Materialize an inline Lambda body (`Code.ZipFile`) to a tmpdir and
|
|
97041
97158
|
* return the directory the container should mount at /var/task. The
|
|
97042
97159
|
* filename is derived from the function's Handler property and the
|
|
@@ -97058,9 +97175,19 @@ function materializeInlineCode(handler, source, fileExtension) {
|
|
|
97058
97175
|
if (lastDot <= 0) throw new Error(`Handler '${handler}' is malformed: expected '<modulePath>.<exportName>'.`);
|
|
97059
97176
|
const modulePath = handler.substring(0, lastDot);
|
|
97060
97177
|
const dir = mkdtempSync(path.join(tmpdir(), "cdkd-local-invoke-"));
|
|
97061
|
-
|
|
97062
|
-
|
|
97063
|
-
|
|
97178
|
+
try {
|
|
97179
|
+
const filePath = resolveInlineCodeFilePath(dir, modulePath, fileExtension, handler);
|
|
97180
|
+
mkdirSync(path.dirname(filePath), { recursive: true });
|
|
97181
|
+
writeFileSync(filePath, source, "utf-8");
|
|
97182
|
+
} catch (e) {
|
|
97183
|
+
try {
|
|
97184
|
+
rmSync(dir, {
|
|
97185
|
+
recursive: true,
|
|
97186
|
+
force: true
|
|
97187
|
+
});
|
|
97188
|
+
} catch {}
|
|
97189
|
+
throw e;
|
|
97190
|
+
}
|
|
97064
97191
|
return dir;
|
|
97065
97192
|
}
|
|
97066
97193
|
/**
|
|
@@ -97234,4 +97361,4 @@ function buildProgram() {
|
|
|
97234
97361
|
|
|
97235
97362
|
//#endregion
|
|
97236
97363
|
export { buildProgram };
|
|
97237
|
-
//# sourceMappingURL=program-
|
|
97364
|
+
//# sourceMappingURL=program-CGhtrsw6.js.map
|