@go-to-k/cdkd 0.290.41 → 0.290.43
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-CPWB3m4b.js → asg-provider-C9R-ND4b.js} +2 -2
- package/dist/{asg-provider-CPWB3m4b.js.map → asg-provider-C9R-ND4b.js.map} +1 -1
- package/dist/cli.js +2 -2
- package/dist/{deploy-engine-RoGuHEgf.js → deploy-engine-Bw8VoyEi.js} +488 -125
- package/dist/deploy-engine-Bw8VoyEi.js.map +1 -0
- package/dist/index.d.ts +36 -34
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/{program-B1JOjOY4.js → program-Bh8a5wjj.js} +169 -54
- package/dist/{program-B1JOjOY4.js.map → program-Bh8a5wjj.js.map} +1 -1
- package/dist/{version-Cxppiepi.js → version-ChfV4K6h.js} +2 -2
- package/dist/{version-Cxppiepi.js.map → version-ChfV4K6h.js.map} +1 -1
- package/package.json +4 -4
- package/dist/deploy-engine-RoGuHEgf.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { C as displayAwsMessage, D as truncateCodePoints, E as isPasteableIdent, S as UNRENDERABLE, T as displaySafe, d as applyDefaultNameForFallback, f as explicitNamePropertyFor, g as looksLikeCdkdGeneratedName, h as getCurrentStackName, m as generateResourceNameWithFallback, n as getLogger, p as generateResourceName, r as isStdoutReservedForPayload, s as getLiveRenderer, v as withStackName, w as displayIdent, x as STACK_REF_MAX_CODE_POINTS, y as withoutGeneratedFallbackName } from "./logger-D-2HeLmd.js";
|
|
2
2
|
import { a as setAssumedRoleCredentials, i as getPreAssumeEnvCredentials, n as awsClientDefaults, o as setPreAssumeEnvCredentials, r as getAssumedRoleCredentials, t as SECOND_ROLE_PUBLISH_REFUSAL } from "./aws-client-defaults-BoIxyCir.js";
|
|
3
|
-
import { t as getCdkdVersion } from "./version-
|
|
3
|
+
import { t as getCdkdVersion } from "./version-ChfV4K6h.js";
|
|
4
4
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5
5
|
import { createHash, randomUUID } from "node:crypto";
|
|
6
6
|
import { CreateBucketCommand, DeleteObjectCommand, DeleteObjectsCommand, GetBucketLocationCommand, GetBucketReplicationCommand, GetObjectCommand, HeadBucketCommand, HeadObjectCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchKey, PutBucketEncryptionCommand, PutBucketPolicyCommand, PutObjectCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
|
|
@@ -25,7 +25,9 @@ import { CreateClusterSnapshotCommand, DescribeClusterSnapshotsCommand, Describe
|
|
|
25
25
|
import { CreateSnapshotCommand as CreateSnapshotCommand$1, DescribeReplicationGroupsCommand, DescribeSnapshotsCommand as DescribeSnapshotsCommand$1, ElastiCacheClient } from "@aws-sdk/client-elasticache";
|
|
26
26
|
import { ACMClient } from "@aws-sdk/client-acm";
|
|
27
27
|
import { LambdaMicrovmsClient } from "@aws-sdk/client-lambda-microvms";
|
|
28
|
+
import * as fs from "node:fs";
|
|
28
29
|
import { createReadStream, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
30
|
+
import * as path from "node:path";
|
|
29
31
|
import { basename, isAbsolute, join, resolve } from "node:path";
|
|
30
32
|
import { spawn } from "node:child_process";
|
|
31
33
|
import { homedir, tmpdir } from "node:os";
|
|
@@ -33,7 +35,6 @@ import { GetHostedZoneCommand, ListHostedZonesByNameCommand, Route53Client } fro
|
|
|
33
35
|
import { DescribeListenersCommand, DescribeLoadBalancersCommand, ElasticLoadBalancingV2Client } from "@aws-sdk/client-elastic-load-balancing-v2";
|
|
34
36
|
import { KMSClient, ListAliasesCommand } from "@aws-sdk/client-kms";
|
|
35
37
|
import { readFile } from "fs/promises";
|
|
36
|
-
import { join as join$1 } from "path";
|
|
37
38
|
import { CreateRepositoryCommand, DescribeImagesCommand as DescribeImagesCommand$1, DescribeRepositoriesCommand, ECRClient, GetAuthorizationTokenCommand, PutImageTagMutabilityCommand } from "@aws-sdk/client-ecr";
|
|
38
39
|
import { inspect } from "node:util";
|
|
39
40
|
import { hostname } from "os";
|
|
@@ -2803,6 +2804,231 @@ function parseEnvironment(env) {
|
|
|
2803
2804
|
};
|
|
2804
2805
|
}
|
|
2805
2806
|
|
|
2807
|
+
//#endregion
|
|
2808
|
+
//#region src/utils/assembly-path.ts
|
|
2809
|
+
/**
|
|
2810
|
+
* `true` when `candidate` names something strictly beneath `base`, both given
|
|
2811
|
+
* as already-resolved absolute paths.
|
|
2812
|
+
*
|
|
2813
|
+
* The `..` test is SEPARATOR-AWARE rather than a bare `startsWith('..')`,
|
|
2814
|
+
* which would also reject a legitimate sibling named `..foo`. The empty-string
|
|
2815
|
+
* case is `base` itself: a directory is never a template, a manifest or a
|
|
2816
|
+
* metadata side file, so `.` and `./` are refused rather than read.
|
|
2817
|
+
*/
|
|
2818
|
+
function isInside(base, candidate) {
|
|
2819
|
+
const rel = path.relative(base, candidate);
|
|
2820
|
+
if (rel === "" || rel === "..") return false;
|
|
2821
|
+
if (rel.startsWith(`..${path.sep}`)) return false;
|
|
2822
|
+
return !path.isAbsolute(rel);
|
|
2823
|
+
}
|
|
2824
|
+
/**
|
|
2825
|
+
* `realpath(3)`, or `undefined` for a path that does not fully resolve.
|
|
2826
|
+
*
|
|
2827
|
+
* `.native` IS load-bearing and must not be simplified to `fs.realpathSync`.
|
|
2828
|
+
* The plain form is a JavaScript walker that folds `..` LEXICALLY — the same
|
|
2829
|
+
* mistake this module's own model branch is careful not to make — so it
|
|
2830
|
+
* disagrees with the kernel on a target carrying a `..` after a symlinked
|
|
2831
|
+
* component, in both directions:
|
|
2832
|
+
*
|
|
2833
|
+
* - It answers `ENOENT` for a path the kernel resolves and `readFileSync`
|
|
2834
|
+
* then reads. With `cdk.out/a -> <outside>/sub` and
|
|
2835
|
+
* `cdk.out/LINK.json -> a/../c.json` (both live), the JS walker folds to
|
|
2836
|
+
* `cdk.out/c.json`, finds nothing, and the verdict falls through to the
|
|
2837
|
+
* model — which folds the same way and calls it CONTAINED while the read
|
|
2838
|
+
* returns `<outside>/c.json`. That is go-to-k/cdkd#3489's original defect,
|
|
2839
|
+
* reopened by the guard meant to close it.
|
|
2840
|
+
* - On a case-insensitive filesystem (APFS by default) the same shape can make
|
|
2841
|
+
* it SPIN. A `try`/`catch` cannot catch a spin, so a hand-modified assembly
|
|
2842
|
+
* hung the very commands this check protects.
|
|
2843
|
+
*
|
|
2844
|
+
* `.native` is libuv's `realpath(3)` (`GetFinalPathNameByHandle` on Windows),
|
|
2845
|
+
* available since Node 8 against a floor of 22, and throws the same
|
|
2846
|
+
* `ENOENT` / `ELOOP` / `EACCES`. Using it is what makes the "exact whenever
|
|
2847
|
+
* the path resolves" claim above TRUE rather than aspirational.
|
|
2848
|
+
*/
|
|
2849
|
+
function tryRealpath(p) {
|
|
2850
|
+
try {
|
|
2851
|
+
return fs.realpathSync.native(p);
|
|
2852
|
+
} catch {
|
|
2853
|
+
return;
|
|
2854
|
+
}
|
|
2855
|
+
}
|
|
2856
|
+
/** `fs.readlinkSync`, or `undefined` when `p` is not a symbolic link. */
|
|
2857
|
+
function tryReadlink(p) {
|
|
2858
|
+
try {
|
|
2859
|
+
return fs.readlinkSync(p);
|
|
2860
|
+
} catch {
|
|
2861
|
+
return;
|
|
2862
|
+
}
|
|
2863
|
+
}
|
|
2864
|
+
/**
|
|
2865
|
+
* Link follows before the walk gives up and reports the path as unresolvable.
|
|
2866
|
+
* It does NOT refuse: an exhausted budget answers `undefined`, which leaves
|
|
2867
|
+
* the symlink arm silent and the lexical verdict standing. That is safe
|
|
2868
|
+
* because the OS gives up first — macOS caps at 32 (`ELOOP`) — so a chain that
|
|
2869
|
+
* reaches this cap is one nothing can read or create through anyway.
|
|
2870
|
+
*/
|
|
2871
|
+
const MAX_LINK_HOPS = 40;
|
|
2872
|
+
/**
|
|
2873
|
+
* Unresolvable path COMPONENTS the climb walks before giving up. Bounds the
|
|
2874
|
+
* recursion below, which is one frame per component: 20 000 components raised
|
|
2875
|
+
* an uncaught `RangeError` and 5 000 cost ~200 ms per call. Like the hop cap
|
|
2876
|
+
* this gives up rather than refusing, which is safe for the same reason —
|
|
2877
|
+
* nothing can read or create through a path that deep.
|
|
2878
|
+
*/
|
|
2879
|
+
const MAX_PATH_COMPONENTS = 1e3;
|
|
2880
|
+
/**
|
|
2881
|
+
* Where `target` REALLY points, for a path that may not exist yet.
|
|
2882
|
+
*
|
|
2883
|
+
* `fs.realpathSync` answers only for a path that fully resolves, and it throws
|
|
2884
|
+
* `ENOENT` for a DANGLING symbolic link exactly as it does for an absent file
|
|
2885
|
+
* — the two are indistinguishable from its result. That difference is the
|
|
2886
|
+
* whole point here: a write FOLLOWS a dangling link and creates the file at
|
|
2887
|
+
* its target, so `cdk.out/Foo.template.json -> ~/.ssh/authorized_keys` (target
|
|
2888
|
+
* absent) is an out-of-directory write that a realpath-only check reports as
|
|
2889
|
+
* contained. Measured: `cdkd synth --verbose` created the victim file.
|
|
2890
|
+
*
|
|
2891
|
+
* So each unresolvable component is handled by hand: climb to the deepest
|
|
2892
|
+
* ancestor that DOES resolve, then re-apply the remaining components,
|
|
2893
|
+
* following any symbolic link with `readlink` and re-resolving its target.
|
|
2894
|
+
* `undefined` means the walk could not resolve the path at all, which for
|
|
2895
|
+
* every caller means their own open fails too.
|
|
2896
|
+
*
|
|
2897
|
+
* KNOW WHAT THIS IS EXACT ABOUT, because the distinction is what bounds it:
|
|
2898
|
+
*
|
|
2899
|
+
* - For a path that FULLY RESOLVES, the answer is `fs.realpathSync`'s and is
|
|
2900
|
+
* therefore exact. Every READ site is in this case by construction — a file
|
|
2901
|
+
* that does not resolve cannot be read — so the containment verdict there is
|
|
2902
|
+
* the kernel's, not this function's. The BASE side is exact there too: every
|
|
2903
|
+
* one is either user-supplied (`-a`, `--output`, `cdkOutDir`) or the
|
|
2904
|
+
* `dirname` of a file cdkd has just read successfully, so neither operand of
|
|
2905
|
+
* a read is in the model's territory.
|
|
2906
|
+
* - For a path that does NOT exist yet, this is a best-effort MODEL of kernel
|
|
2907
|
+
* resolution, and a model can be wrong at an edge. The known one: `..` INSIDE
|
|
2908
|
+
* an unresolvable link's target is folded lexically here, while the kernel
|
|
2909
|
+
* folds it only after following each preceding component, so a target of
|
|
2910
|
+
* `a/../c.json` under a symlinked `a` diverges.
|
|
2911
|
+
*
|
|
2912
|
+
* That edge decides which of the TWO sites that WRITE needs a second check,
|
|
2913
|
+
* and the difference is the BASE rather than the act of writing:
|
|
2914
|
+
*
|
|
2915
|
+
* - `resolveVerboseTemplatePath` (`src/cli/commands/synth.ts`) writes into
|
|
2916
|
+
* `--output`, which may be a pre-existing directory the assembly's author
|
|
2917
|
+
* supplied, so the leaf CAN already be a symbolic link. It therefore asks
|
|
2918
|
+
* the kernel directly with `lstat` and refuses one whatever it points at. A
|
|
2919
|
+
* path-string model can be argued around; `lstat` cannot.
|
|
2920
|
+
* - `resolveInlineCodeFilePath` (`src/cli/commands/local-invoke.ts`) writes
|
|
2921
|
+
* into a `mkdtemp` cdkd has just created, mode 0700 and empty, so no
|
|
2922
|
+
* component of the candidate can be a symbolic link at all and the edge is
|
|
2923
|
+
* unreachable. It needs no `lstat`.
|
|
2924
|
+
*
|
|
2925
|
+
* **A third write site over a base cdkd did not create needs the `lstat`**,
|
|
2926
|
+
* and inherits nothing from this function that would give it one.
|
|
2927
|
+
*
|
|
2928
|
+
* That second site is also the repo's first caller to `mkdirSync(...,
|
|
2929
|
+
* { recursive: true })` through a resolved path, which an earlier revision of
|
|
2930
|
+
* this comment leaned on not existing. It is still safe: the climb charges no
|
|
2931
|
+
* hop for a component, and exhausting `MAX_PATH_COMPONENTS` gives up into the
|
|
2932
|
+
* lexical verdict, which a freshly created directory has no link to defeat.
|
|
2933
|
+
*/
|
|
2934
|
+
function resolveThroughLinks(target, hops = 0, climbs = 0) {
|
|
2935
|
+
const direct = tryRealpath(target);
|
|
2936
|
+
if (direct !== void 0) return direct;
|
|
2937
|
+
if (hops >= MAX_LINK_HOPS) return void 0;
|
|
2938
|
+
if (climbs >= MAX_PATH_COMPONENTS) return void 0;
|
|
2939
|
+
const parent = path.dirname(target);
|
|
2940
|
+
if (parent === target) return void 0;
|
|
2941
|
+
const realParent = resolveThroughLinks(parent, hops, climbs + 1);
|
|
2942
|
+
if (realParent === void 0) return void 0;
|
|
2943
|
+
const link = tryReadlink(target);
|
|
2944
|
+
if (link === void 0) return path.join(realParent, path.basename(target));
|
|
2945
|
+
return resolveThroughLinks(path.resolve(realParent, link), hops + 1, climbs);
|
|
2946
|
+
}
|
|
2947
|
+
/**
|
|
2948
|
+
* Resolve an assembly-supplied `candidate` against `dir` and report whether
|
|
2949
|
+
* the result stays inside it.
|
|
2950
|
+
*
|
|
2951
|
+
* The lexical arm joins exactly the way the call sites used to
|
|
2952
|
+
* (`path.join`, NOT `path.resolve`), so the verdict is about the path the
|
|
2953
|
+
* caller will actually open. `join`'s handling of an absolute candidate makes
|
|
2954
|
+
* this arm strictly more permissive than a `resolve`-based one would be; an
|
|
2955
|
+
* absolute value therefore stays a matter for each site's own tripwire.
|
|
2956
|
+
*
|
|
2957
|
+
* The SYMLINK arm exists because the lexical arm alone leaves an equivalent
|
|
2958
|
+
* hole: `cdk.out/link -> /etc` plus a candidate of `link/passwd` is lexically
|
|
2959
|
+
* contained and still reads `/etc/passwd`. Both sides go through
|
|
2960
|
+
* {@link resolveThroughLinks}, so an assembly directory REACHED through a link
|
|
2961
|
+
* (macOS spells `/tmp` as `/private/tmp`; a user may symlink `cdk.out` itself)
|
|
2962
|
+
* is unaffected, while a candidate that does not exist YET — the shape
|
|
2963
|
+
* `cdkd synth --verbose` writes, and the shape a DANGLING link presents — is
|
|
2964
|
+
* still resolved to where it would actually land. A real `cdk synth` writes no symbolic link
|
|
2965
|
+
* along any of these paths (measured against a CDK 2.268 assembly carrying a
|
|
2966
|
+
* Stage, two nesting levels and an asset manifest), so the arm costs a
|
|
2967
|
+
* legitimate assembly nothing.
|
|
2968
|
+
*
|
|
2969
|
+
* Windows shapes need no special case HERE: `path.relative` is the platform's
|
|
2970
|
+
* own, so a drive-relative or UNC candidate is judged by `path.win32` on
|
|
2971
|
+
* Windows, while on POSIX `C:\evil` and `..\..\evil` are single filename
|
|
2972
|
+
* components that no `readFileSync` can follow out of the directory.
|
|
2973
|
+
*
|
|
2974
|
+
* The verdict is about the assembly AS IT SITS ON DISK, which is the whole
|
|
2975
|
+
* threat model: a hand-modified or third-party `cdk.out` the user then points
|
|
2976
|
+
* cdkd at. It is NOT a defence against a process rewriting that directory
|
|
2977
|
+
* concurrently — the caller opens by path after this returns, so a link
|
|
2978
|
+
* swapped in between is outside what any check here can see.
|
|
2979
|
+
*/
|
|
2980
|
+
function resolveAssemblyPath(dir, candidate, options) {
|
|
2981
|
+
const base = path.resolve(dir);
|
|
2982
|
+
const bound = options?.containWithin === void 0 ? base : path.resolve(options.containWithin);
|
|
2983
|
+
const joined = path.resolve(path.join(base, candidate));
|
|
2984
|
+
if (!isInside(bound, joined)) return {
|
|
2985
|
+
contained: false,
|
|
2986
|
+
escape: "lexical",
|
|
2987
|
+
path: joined
|
|
2988
|
+
};
|
|
2989
|
+
const realBase = resolveThroughLinks(bound);
|
|
2990
|
+
if (realBase !== void 0) {
|
|
2991
|
+
const realTarget = resolveThroughLinks(joined);
|
|
2992
|
+
if (realTarget !== void 0 && !isInside(realBase, realTarget)) return {
|
|
2993
|
+
contained: false,
|
|
2994
|
+
escape: "symlink",
|
|
2995
|
+
path: joined,
|
|
2996
|
+
realPath: realTarget
|
|
2997
|
+
};
|
|
2998
|
+
}
|
|
2999
|
+
return {
|
|
3000
|
+
contained: true,
|
|
3001
|
+
path: joined
|
|
3002
|
+
};
|
|
3003
|
+
}
|
|
3004
|
+
/**
|
|
3005
|
+
* The shared tail of every containment refusal: what the value resolved to,
|
|
3006
|
+
* what it escaped, and why that means the assembly is not CDK-generated. Each
|
|
3007
|
+
* call site supplies its own subject ("Stack 'X' has templateFile='...' which
|
|
3008
|
+
* ") and its own error class, because the sites throw four different types.
|
|
3009
|
+
* `action` completes "Refusing to ..." for a caller that declines something
|
|
3010
|
+
* other than loading the file — `renderNestedTemplateTreeDefect` says "deploy"
|
|
3011
|
+
* or "diff", matching its own sibling refusals.
|
|
3012
|
+
*
|
|
3013
|
+
* Every interpolation goes through `displaySafe` for the reason
|
|
3014
|
+
* `AssemblyReader`'s own refusals give (go-to-k/cdkd#3277): this text exists
|
|
3015
|
+
* FOR a hand-modified assembly, so the candidate, the resolved path and even
|
|
3016
|
+
* `dir` (below a Stage it derives from the manifest's `directoryName`) are all
|
|
3017
|
+
* attacker-chosen, and `formatError` sanitizes only an error's `cause`, never
|
|
3018
|
+
* its own `message`.
|
|
3019
|
+
*/
|
|
3020
|
+
function renderAssemblyPathEscape(escape, dir, action = "load", provenanceOverride) {
|
|
3021
|
+
const provenance = provenanceOverride ?? `CDK emits assembly paths that stay inside the assembly directory; one that leaves it indicates the synth output was hand-modified or generated by a non-CDK toolchain. Refusing to ${action}.`;
|
|
3022
|
+
const base = path.resolve(dir);
|
|
3023
|
+
const realBase = resolveThroughLinks(base) ?? base;
|
|
3024
|
+
if (escape.escape === "symlink") {
|
|
3025
|
+
if (escape.realPath === realBase) return `resolves to '${displaySafe(escape.path)}', a symbolic link to the directory '${displaySafe(base)}' itself rather than to a file inside it. ${provenance}`;
|
|
3026
|
+
return `resolves to '${displaySafe(escape.path)}', which leads through a symbolic link to '${displaySafe(escape.realPath)}', outside '${displaySafe(base)}'. ${provenance}`;
|
|
3027
|
+
}
|
|
3028
|
+
if (escape.path === base) return `names the directory '${displaySafe(base)}' itself rather than a file inside it. ${provenance}`;
|
|
3029
|
+
return `resolves to '${displaySafe(escape.path)}', outside '${displaySafe(base)}'. ${provenance}`;
|
|
3030
|
+
}
|
|
3031
|
+
|
|
2806
3032
|
//#endregion
|
|
2807
3033
|
//#region src/synthesis/stack-messages.ts
|
|
2808
3034
|
/**
|
|
@@ -2833,7 +3059,9 @@ const MESSAGE_ENTRY_TYPES = {
|
|
|
2833
3059
|
function collectStackMessages(assemblyDir, artifact) {
|
|
2834
3060
|
const merged = { ...artifact.metadata ?? {} };
|
|
2835
3061
|
if (artifact.additionalMetadataFile) {
|
|
2836
|
-
const
|
|
3062
|
+
const resolved = resolveAssemblyPath(assemblyDir, artifact.additionalMetadataFile);
|
|
3063
|
+
if (!resolved.contained) throw new SynthesisError(`Stack metadata file '${displaySafe(artifact.additionalMetadataFile)}' ${renderAssemblyPathEscape(resolved, assemblyDir)}`);
|
|
3064
|
+
const metadataPath = resolved.path;
|
|
2837
3065
|
let sideFile;
|
|
2838
3066
|
try {
|
|
2839
3067
|
sideFile = JSON.parse(readFileSync(metadataPath, "utf-8"));
|
|
@@ -2935,7 +3163,7 @@ var AssemblyReader = class {
|
|
|
2935
3163
|
/**
|
|
2936
3164
|
* Get all stacks from assembly (recursively traverses nested assemblies / Stages)
|
|
2937
3165
|
*/
|
|
2938
|
-
getAllStacks(assemblyDir, manifest) {
|
|
3166
|
+
getAllStacks(assemblyDir, manifest, assetOutdir = assemblyDir) {
|
|
2939
3167
|
if (!manifest.artifacts) {
|
|
2940
3168
|
this.logger.warn("No artifacts found in manifest");
|
|
2941
3169
|
return [];
|
|
@@ -2943,15 +3171,17 @@ var AssemblyReader = class {
|
|
|
2943
3171
|
const assetManifestMap = this.buildAssetManifestMap(assemblyDir, manifest);
|
|
2944
3172
|
const stacks = [];
|
|
2945
3173
|
for (const [artifactId, artifact] of Object.entries(manifest.artifacts)) if (artifact.type === "aws:cloudformation:stack") {
|
|
2946
|
-
const stackInfo = this.extractStackInfo(assemblyDir, artifactId, artifact, manifest, assetManifestMap);
|
|
3174
|
+
const stackInfo = this.extractStackInfo(assemblyDir, artifactId, artifact, manifest, assetManifestMap, assetOutdir);
|
|
2947
3175
|
stacks.push(stackInfo);
|
|
2948
3176
|
} else if (artifact.type === "cdk:cloud-assembly") {
|
|
2949
3177
|
const props = artifact.properties;
|
|
2950
3178
|
if (props?.directoryName) {
|
|
2951
|
-
const
|
|
3179
|
+
const resolved = resolveAssemblyPath(assemblyDir, props.directoryName);
|
|
3180
|
+
if (!resolved.contained) throw new SynthesisError(`Nested assembly '${displaySafe(props.directoryName)}' ${renderAssemblyPathEscape(resolved, assemblyDir)}`);
|
|
3181
|
+
const nestedDir = resolved.path;
|
|
2952
3182
|
try {
|
|
2953
3183
|
const nestedManifest = this.readManifest(nestedDir);
|
|
2954
|
-
const nestedStacks = this.getAllStacks(nestedDir, nestedManifest);
|
|
3184
|
+
const nestedStacks = this.getAllStacks(nestedDir, nestedManifest, assetOutdir);
|
|
2955
3185
|
stacks.push(...nestedStacks);
|
|
2956
3186
|
} catch (error) {
|
|
2957
3187
|
this.logger.warn(`Failed to read nested assembly '${displaySafe(props.directoryName)}': ${displaySafe(error instanceof Error ? error.message : String(error))}`);
|
|
@@ -2985,19 +3215,25 @@ var AssemblyReader = class {
|
|
|
2985
3215
|
for (const [artifactId, artifact] of Object.entries(manifest.artifacts)) {
|
|
2986
3216
|
if (artifact.type !== "cdk:asset-manifest") continue;
|
|
2987
3217
|
const props = artifact.properties;
|
|
2988
|
-
if (props?.file)
|
|
3218
|
+
if (props?.file) {
|
|
3219
|
+
const resolved = resolveAssemblyPath(assemblyDir, props.file);
|
|
3220
|
+
if (!resolved.contained) throw new SynthesisError(`Asset manifest artifact '${displaySafe(artifactId)}' has file='${displaySafe(props.file)}' which ${renderAssemblyPathEscape(resolved, assemblyDir)}`);
|
|
3221
|
+
map.set(artifactId, resolved.path);
|
|
3222
|
+
}
|
|
2989
3223
|
}
|
|
2990
3224
|
return map;
|
|
2991
3225
|
}
|
|
2992
3226
|
/**
|
|
2993
3227
|
* Extract stack info from artifact
|
|
2994
3228
|
*/
|
|
2995
|
-
extractStackInfo(assemblyDir, artifactId, artifact, manifest, assetManifestMap) {
|
|
3229
|
+
extractStackInfo(assemblyDir, artifactId, artifact, manifest, assetManifestMap, assetOutdir) {
|
|
2996
3230
|
const props = artifact.properties;
|
|
2997
3231
|
const stackName = props?.stackName || artifactId;
|
|
2998
3232
|
const templateFile = props?.templateFile;
|
|
2999
3233
|
if (!templateFile) throw new SynthesisError(`Stack '${displaySafe(stackName)}' has no templateFile property`);
|
|
3000
|
-
const
|
|
3234
|
+
const resolvedTemplate = resolveAssemblyPath(assemblyDir, templateFile);
|
|
3235
|
+
if (!resolvedTemplate.contained) throw new SynthesisError(`Stack '${displaySafe(stackName)}' has templateFile='${displaySafe(templateFile)}' which ${renderAssemblyPathEscape(resolvedTemplate, assemblyDir)}`);
|
|
3236
|
+
const templatePath = resolvedTemplate.path;
|
|
3001
3237
|
let template;
|
|
3002
3238
|
try {
|
|
3003
3239
|
const content = readFileSync(templatePath, "utf-8");
|
|
@@ -3031,7 +3267,9 @@ var AssemblyReader = class {
|
|
|
3031
3267
|
const assetPath = resource.Metadata?.["aws:asset:path"];
|
|
3032
3268
|
if (typeof assetPath !== "string" || assetPath.length === 0) continue;
|
|
3033
3269
|
if (isAbsolute(assetPath) || /^[a-zA-Z]:[\\/]/.test(assetPath) || assetPath.startsWith("\\\\")) throw new SynthesisError(`Stack '${displaySafe(stackName)}' 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.`);
|
|
3034
|
-
|
|
3270
|
+
const resolvedNested = resolveAssemblyPath(assemblyDir, assetPath);
|
|
3271
|
+
if (!resolvedNested.contained) throw new SynthesisError(`Stack '${displaySafe(stackName)}' nested-stack '${displaySafe(logicalId)}' has Metadata['aws:asset:path']='${displaySafe(assetPath)}' which ${renderAssemblyPathEscape(resolvedNested, assemblyDir)}`);
|
|
3272
|
+
nestedTemplates[logicalId] = resolvedNested.path;
|
|
3035
3273
|
}
|
|
3036
3274
|
return {
|
|
3037
3275
|
stackName,
|
|
@@ -3039,6 +3277,7 @@ var AssemblyReader = class {
|
|
|
3039
3277
|
artifactId,
|
|
3040
3278
|
template,
|
|
3041
3279
|
assetManifestPath,
|
|
3280
|
+
assetOutdir,
|
|
3042
3281
|
dependencyNames,
|
|
3043
3282
|
region: env?.region !== "unknown-region" ? env?.region : void 0,
|
|
3044
3283
|
account: env?.account !== "unknown-account" ? env?.account : void 0,
|
|
@@ -4411,6 +4650,141 @@ function decodeListingKey(value) {
|
|
|
4411
4650
|
}
|
|
4412
4651
|
}
|
|
4413
4652
|
|
|
4653
|
+
//#endregion
|
|
4654
|
+
//#region src/state/record-keys.ts
|
|
4655
|
+
/**
|
|
4656
|
+
* The ONE encoding anything that identifies a producer-side thing by a pair of
|
|
4657
|
+
* runtime strings goes through — a warned-once `Set`, a dedupe `Set`, a
|
|
4658
|
+
* memoization `Map`, a tree's node index.
|
|
4659
|
+
*
|
|
4660
|
+
* An import-free LEAF, the same shape as `src/utils/display-safe.ts` and
|
|
4661
|
+
* `src/utils/parameter-types.ts`, and that is load-bearing rather than tidy:
|
|
4662
|
+
* the consumers span the `cli`, `state` and `deployment` layers, and one of
|
|
4663
|
+
* them — `src/cli/commands/state-list-tree.ts` — is deliberately kept with NO
|
|
4664
|
+
* imports at all so the tree-building logic is unit-testable without mocking
|
|
4665
|
+
* anything. These functions previously lived in `malformed-resources-bag.ts`,
|
|
4666
|
+
* which reaches `error-handler` / `display-safe` / `lock-contention-message`,
|
|
4667
|
+
* and that module still RE-EXPORTS them so no existing importer moved.
|
|
4668
|
+
*
|
|
4669
|
+
* ENCODED, not separated. Issue
|
|
4670
|
+
* [#3308](https://github.com/go-to-k/cdkd/issues/3308) is the measurement
|
|
4671
|
+
* behind that and issue [#3323](https://github.com/go-to-k/cdkd/issues/3323)
|
|
4672
|
+
* is the one that found how far the class ran. Both halves of every key here
|
|
4673
|
+
* are attacker-influenced, and a separator only NARROWS a collision: with a
|
|
4674
|
+
* NUL separator, stack `Evil<NUL>us-east-1` in `ap-northeast-1` and stack
|
|
4675
|
+
* `Evil` in region `us-east-1<NUL>ap-northeast-1` produce the SAME key.
|
|
4676
|
+
*
|
|
4677
|
+
* `JSON.stringify` of a two-element array is injective over string pairs: the
|
|
4678
|
+
* quoting escapes anything that could imitate the separator, so no planted
|
|
4679
|
+
* value can produce another pair's key.
|
|
4680
|
+
*
|
|
4681
|
+
* **What a collision COSTS is per SITE, not a property of the key, and a note
|
|
4682
|
+
* here once claimed otherwise** — "one dropped warning LINE, never a wrong
|
|
4683
|
+
* resolution". That held for the two warned-once sets it was written for and
|
|
4684
|
+
* is false at three `cdkd scrub` sites, where a collision is a wrong ANSWER
|
|
4685
|
+
* feeding the pre-pass that decides whether a producer still holds plaintext.
|
|
4686
|
+
* Do not restore the narrower sentence: it reads as a licence to leave a
|
|
4687
|
+
* separator at a site nobody has re-checked.
|
|
4688
|
+
*
|
|
4689
|
+
* **Whether a separator was EVER injective is also per site**, because it
|
|
4690
|
+
* depends on each half's PROVENANCE. A half read out of an S3 key segment
|
|
4691
|
+
* cannot carry a NUL — measured on go-to-k/cdkd#3323, S3 refuses to store such
|
|
4692
|
+
* a key — so a key whose LEFT half is a segment splits uniquely at the FIRST
|
|
4693
|
+
* NUL. **The rule is two-sided and the dual is easy to miss**: a NUL-free
|
|
4694
|
+
* RIGHT half splits uniquely at the LAST one, which is what actually held at
|
|
4695
|
+
* one of the sites here. A half read out of a TEMPLATE or a state body is
|
|
4696
|
+
* gated by neither.
|
|
4697
|
+
*
|
|
4698
|
+
* Do not settle a site by asking whether the SEPARATOR is storable. That
|
|
4699
|
+
* question was asked twice on go-to-k/cdkd#3323 and gave the wrong answer both
|
|
4700
|
+
* times, in opposite directions. Encoding removes the need to ask at all,
|
|
4701
|
+
* which is why it is the fix rather than a wider separator.
|
|
4702
|
+
*/
|
|
4703
|
+
/** The ONE spelling of the encoding, private so no caller can re-derive it. */
|
|
4704
|
+
function injectivePairKey(first, second) {
|
|
4705
|
+
return JSON.stringify([first, second]);
|
|
4706
|
+
}
|
|
4707
|
+
/**
|
|
4708
|
+
* The same rule for a key of MORE than two parts.
|
|
4709
|
+
*
|
|
4710
|
+
* The named wrappers above stay, because a `(stack, region)` record and a
|
|
4711
|
+
* `(stack, export)` coordinate are things worth naming. This one is for the
|
|
4712
|
+
* keys that are just a tuple — an idempotency-token memo key, a resource-pair
|
|
4713
|
+
* edge — where a name would add nothing and a positional list is the honest
|
|
4714
|
+
* shape. It is exported rather than private for the same reason the two are:
|
|
4715
|
+
* so a call site can adopt the rule without re-spelling the encoding
|
|
4716
|
+
* (go-to-k/cdkd#3496).
|
|
4717
|
+
*
|
|
4718
|
+
* `number` is accepted because several of these tuples carry one, and
|
|
4719
|
+
* stringifying at the call site would put the coercion back where a caller can
|
|
4720
|
+
* get it wrong. `JSON.stringify` distinguishes `1` from `"1"`, so a mixed
|
|
4721
|
+
* tuple stays injective across the two.
|
|
4722
|
+
*
|
|
4723
|
+
* KNOWN HOLE in that, recorded rather than guarded: `NaN`, `Infinity` and
|
|
4724
|
+
* `-Infinity` all stringify to `null`, so those three collapse onto one key
|
|
4725
|
+
* and onto a literal `null`. No call site passes one — every numeric argument
|
|
4726
|
+
* today is a literal bound (`64`, `32`) — and a runtime refusal would turn a
|
|
4727
|
+
* key builder into a throwing path for a value that cannot arrive. Check this
|
|
4728
|
+
* note before passing a COMPUTED number.
|
|
4729
|
+
*
|
|
4730
|
+
* Note what this does to a DIGEST that hashes the result: the encoded string
|
|
4731
|
+
* carries no raw control character at all — `JSON.stringify` escapes a NUL to
|
|
4732
|
+
* the six-character text `\u0000` — so a hash input that separates this key
|
|
4733
|
+
* from its neighbours with a NUL is injective too, without the separator
|
|
4734
|
+
* having to be chosen carefully.
|
|
4735
|
+
*/
|
|
4736
|
+
function injectiveKey(...parts) {
|
|
4737
|
+
return JSON.stringify(parts);
|
|
4738
|
+
}
|
|
4739
|
+
/**
|
|
4740
|
+
* The prefix every {@link injectiveKey} OF TWO OR MORE PARTS whose first part
|
|
4741
|
+
* is `first` begins with. The arity matters and an earlier revision of this
|
|
4742
|
+
* line omitted it: a ONE-part key ends with a bracket where this prefix ends
|
|
4743
|
+
* with a comma, so it does not match one. Both call sites build two parts.
|
|
4744
|
+
*
|
|
4745
|
+
* It exists for the one reader shape an encoded key otherwise breaks: a cache that
|
|
4746
|
+
* evicts by scanning its keys for a leading component.
|
|
4747
|
+
*
|
|
4748
|
+
* DERIVED from the same encoder rather than spelled again, and that is the
|
|
4749
|
+
* whole point. go-to-k/cdkd#3496's first cut encoded three provider caches and
|
|
4750
|
+
* left `invalidateAttributeCache` testing the OLD `<physicalId>:` prefix, so
|
|
4751
|
+
* the scan matched nothing and every post-update `Fn::GetAtt` read the
|
|
4752
|
+
* pre-update value — silently, with every gate green. A prefix computed here
|
|
4753
|
+
* cannot drift from the key computed two functions up.
|
|
4754
|
+
*
|
|
4755
|
+
* Exact, not a heuristic: `JSON.stringify([first])` quotes and escapes `first`,
|
|
4756
|
+
* so replacing its closing `]` with `,` yields a string no OTHER first part can
|
|
4757
|
+
* begin with. Deriving it by searching the encoded key for a comma would NOT
|
|
4758
|
+
* be exact — a comma inside the value is not escaped by JSON.
|
|
4759
|
+
*/
|
|
4760
|
+
function injectiveKeyPrefix(first) {
|
|
4761
|
+
return JSON.stringify([first]).slice(0, -1) + ",";
|
|
4762
|
+
}
|
|
4763
|
+
/**
|
|
4764
|
+
* The key for a state RECORD: the `(stackName, region)` pair that names one
|
|
4765
|
+
* `state.json`.
|
|
4766
|
+
*/
|
|
4767
|
+
function producerRecordKey(stackName, region) {
|
|
4768
|
+
return injectivePairKey(stackName, region);
|
|
4769
|
+
}
|
|
4770
|
+
/**
|
|
4771
|
+
* The key for a producer COORDINATE: a `(stack, export-or-output name)` pair,
|
|
4772
|
+
* used by `cdkd scrub`'s re-export chain walk and its per-coordinate verdict
|
|
4773
|
+
* cache.
|
|
4774
|
+
*
|
|
4775
|
+
* A separate NAME because the subject is a different thing — a coordinate is
|
|
4776
|
+
* not a record, and a call site reading `producerRecordKey(stack, exportName)`
|
|
4777
|
+
* would say something false. ONE implementation, though, which is the property
|
|
4778
|
+
* the "one spelling" rule is actually about: two names over one implementation
|
|
4779
|
+
* cannot drift, two implementations can.
|
|
4780
|
+
*
|
|
4781
|
+
* An `exportName` is REDACTED on the way into state
|
|
4782
|
+
* (`StateImportEntry`'s JSDoc), so this half may hold any string at all.
|
|
4783
|
+
*/
|
|
4784
|
+
function producerCoordinateKey(stackName, exportOrOutputName) {
|
|
4785
|
+
return injectivePairKey(stackName, exportOrOutputName);
|
|
4786
|
+
}
|
|
4787
|
+
|
|
4414
4788
|
//#endregion
|
|
4415
4789
|
//#region src/state/s3-replication-purge-gap.ts
|
|
4416
4790
|
/**
|
|
@@ -4730,7 +5104,7 @@ async function runProbe(s3Client, bucket, requestFields) {
|
|
|
4730
5104
|
}
|
|
4731
5105
|
}
|
|
4732
5106
|
function probe(s3Client, bucket, requestFields) {
|
|
4733
|
-
const key =
|
|
5107
|
+
const key = injectiveKey(bucket, requestFields.ExpectedBucketOwner ?? "");
|
|
4734
5108
|
const cached = replicationProbeCache.get(key);
|
|
4735
5109
|
if (cached) return cached;
|
|
4736
5110
|
const pending = runProbe(s3Client, bucket, requestFields).then((result) => {
|
|
@@ -4790,11 +5164,7 @@ async function warnIfPurgeIsReplicated(s3Client, bucket, keys, options = {}) {
|
|
|
4790
5164
|
const covered = result.rules.filter((rule) => keys.some((key) => key.startsWith(rule.prefix)));
|
|
4791
5165
|
if (covered.length === 0) return;
|
|
4792
5166
|
const destinations = [...new Set(covered.map((rule) => rule.enabled ? rule.destination : `${rule.destination} (rule currently Disabled -- it stops FUTURE replication, not what it already copied)`))].sort();
|
|
4793
|
-
const warnKey =
|
|
4794
|
-
bucket,
|
|
4795
|
-
options.objectDescription ?? "",
|
|
4796
|
-
...destinations
|
|
4797
|
-
].join("\0");
|
|
5167
|
+
const warnKey = injectiveKey(bucket, options.objectDescription ?? "", ...destinations);
|
|
4798
5168
|
if (warnedWarnings.has(warnKey)) {
|
|
4799
5169
|
emitDebug(`S3 replication on s3://${safeBucket} still covers purged keys (${options.objectDescription ?? "the body of an object cdkd has just reported as removed"}); already reported once this run for ${destinations.map((d) => displaySafe(d, { asciiOnly: true })).join(", ")}.`);
|
|
4800
5170
|
return;
|
|
@@ -6235,6 +6605,24 @@ function setsEqual(a, b) {
|
|
|
6235
6605
|
//#endregion
|
|
6236
6606
|
//#region src/assets/asset-manifest-loader.ts
|
|
6237
6607
|
/**
|
|
6608
|
+
* THE one spelling of "where does this file asset's source live", shared by
|
|
6609
|
+
* {@link AssetManifestLoader.getAssetSourcePath} and `FileAssetPublisher`
|
|
6610
|
+
* (issue [#3489](https://github.com/go-to-k/cdkd/issues/3489)).
|
|
6611
|
+
*
|
|
6612
|
+
* Both used to `join(cdkOutputDir, asset.source.path)` independently. The
|
|
6613
|
+
* manifest is chosen by whoever wrote the assembly, `path.join` folds `..`,
|
|
6614
|
+
* and the publisher ZIPs whatever it finds and `PutObject`s it to a bucket the
|
|
6615
|
+
* same manifest names — so an unchecked `source.path` is an exfiltration
|
|
6616
|
+
* primitive with the caller's own credentials, which is the harm the manifest
|
|
6617
|
+
* FILE's own containment check cites. One function so the loader and the
|
|
6618
|
+
* publisher cannot disagree about which file is published.
|
|
6619
|
+
*/
|
|
6620
|
+
function resolveFileAssetSourcePath(manifestDir, asset, assetOutdir = manifestDir) {
|
|
6621
|
+
const resolved = resolveAssemblyPath(manifestDir, asset.source.path, { containWithin: assetOutdir });
|
|
6622
|
+
if (!resolved.contained) throw new Error(`File asset '${displaySafe(asset.displayName)}' has source.path='${displaySafe(asset.source.path)}' which ${renderAssemblyPathEscape(resolved, assetOutdir, "publish it")}`);
|
|
6623
|
+
return resolved.path;
|
|
6624
|
+
}
|
|
6625
|
+
/**
|
|
6238
6626
|
* Whether a file asset's `source.path` is a CloudFormation template asset
|
|
6239
6627
|
* (the stack template `<Stack>.template.json` or a nested-stack template
|
|
6240
6628
|
* `<Stack>.<Nested>.nested.template.json`). cdkd deploys templates itself and
|
|
@@ -6266,7 +6654,9 @@ var AssetManifestLoader = class {
|
|
|
6266
6654
|
* @returns Asset manifest or null if not found
|
|
6267
6655
|
*/
|
|
6268
6656
|
async loadManifest(cdkOutputDir, stackName) {
|
|
6269
|
-
const
|
|
6657
|
+
const resolved = resolveAssemblyPath(cdkOutputDir, `${stackName}.assets.json`);
|
|
6658
|
+
if (!resolved.contained) throw new Error(`Asset manifest for stack '${displaySafe(stackName)}' ${renderAssemblyPathEscape(resolved, cdkOutputDir)}`);
|
|
6659
|
+
const manifestPath = resolved.path;
|
|
6270
6660
|
try {
|
|
6271
6661
|
this.logger.debug(`Loading asset manifest from: ${manifestPath}`);
|
|
6272
6662
|
const content = await readFile(manifestPath, "utf-8");
|
|
@@ -6302,12 +6692,18 @@ var AssetManifestLoader = class {
|
|
|
6302
6692
|
/**
|
|
6303
6693
|
* Get asset source path (absolute path)
|
|
6304
6694
|
*
|
|
6695
|
+
* Refuses a `source.path` resolving outside `cdkOutputDir` (issue
|
|
6696
|
+
* [#3489](https://github.com/go-to-k/cdkd/issues/3489)). The manifest is
|
|
6697
|
+
* assembly-supplied and `cdkd deploy -a <dir>` consumes a pre-synthesized
|
|
6698
|
+
* one, so a `source.path` of `../../../home/<user>/.aws` would otherwise be
|
|
6699
|
+
* packaged and uploaded to a bucket the SAME manifest names.
|
|
6700
|
+
*
|
|
6305
6701
|
* @param cdkOutputDir CDK output directory
|
|
6306
6702
|
* @param asset File asset
|
|
6307
6703
|
* @returns Absolute path to asset source
|
|
6308
6704
|
*/
|
|
6309
|
-
getAssetSourcePath(cdkOutputDir, asset) {
|
|
6310
|
-
return
|
|
6705
|
+
getAssetSourcePath(cdkOutputDir, asset, assetOutdir) {
|
|
6706
|
+
return resolveFileAssetSourcePath(cdkOutputDir, asset, assetOutdir ?? cdkOutputDir);
|
|
6311
6707
|
}
|
|
6312
6708
|
/**
|
|
6313
6709
|
* Resolve asset destination values (replace ${AWS::AccountId}, ${AWS::Region}, etc.)
|
|
@@ -6399,7 +6795,8 @@ var FileAssetPublisher = class {
|
|
|
6399
6795
|
* @param region AWS region
|
|
6400
6796
|
* @param profile AWS profile (optional)
|
|
6401
6797
|
*/
|
|
6402
|
-
async publish(assetHash, asset, cdkOutputDir, accountId, region, _profile) {
|
|
6798
|
+
async publish(assetHash, asset, cdkOutputDir, accountId, region, _profile, assetOutdir) {
|
|
6799
|
+
const sourcePath = resolveFileAssetSourcePath(cdkOutputDir, asset, assetOutdir ?? cdkOutputDir);
|
|
6403
6800
|
for (const [, dest] of Object.entries(asset.destinations)) {
|
|
6404
6801
|
const bucketName = this.resolvePlaceholders(dest.bucketName, accountId, region);
|
|
6405
6802
|
const objectKey = this.resolvePlaceholders(dest.objectKey, accountId, region);
|
|
@@ -6414,7 +6811,6 @@ var FileAssetPublisher = class {
|
|
|
6414
6811
|
this.logger.debug(`Asset already exists, skipping: s3://${bucketName}/${objectKey}`);
|
|
6415
6812
|
continue;
|
|
6416
6813
|
}
|
|
6417
|
-
const sourcePath = join(cdkOutputDir, asset.source.path);
|
|
6418
6814
|
if (asset.source.packaging === "zip") await this.uploadZip(client, sourcePath, bucketName, objectKey);
|
|
6419
6815
|
else await this.uploadFile(client, sourcePath, bucketName, objectKey);
|
|
6420
6816
|
this.logger.debug(`✅ Published: s3://${bucketName}/${objectKey}`);
|
|
@@ -7734,6 +8130,28 @@ function describeDockerCapturedOutput(error, args, fallback) {
|
|
|
7734
8130
|
//#endregion
|
|
7735
8131
|
//#region src/assets/docker-build.ts
|
|
7736
8132
|
/**
|
|
8133
|
+
* Where a Docker asset's build context lives, refusing a `source.directory`
|
|
8134
|
+
* that resolves outside `cdkOutDir` (issue
|
|
8135
|
+
* [#3489](https://github.com/go-to-k/cdkd/issues/3489)).
|
|
8136
|
+
*
|
|
8137
|
+
* The field is assembly-supplied and used BOTH as the executable's cwd and as
|
|
8138
|
+
* the `docker build` cwd, so an escaping value sends a directory from outside
|
|
8139
|
+
* `cdk.out` to BuildKit and bakes it into the image pushed to a repository the
|
|
8140
|
+
* same manifest names. Both sites used raw `${a}/${b}` concatenation, which
|
|
8141
|
+
* honours `..` exactly as `join` does; this is the one spelling they share.
|
|
8142
|
+
*
|
|
8143
|
+
* It throws through the CALLER's `wrapError` so each consumer keeps its own
|
|
8144
|
+
* typed error class, exactly as this module's other pre-docker refusals do.
|
|
8145
|
+
*
|
|
8146
|
+
* Twin of `resolveFileAssetSourcePath` and `resolveVerboseTemplatePath`.
|
|
8147
|
+
* Exported for unit testing.
|
|
8148
|
+
*/
|
|
8149
|
+
function resolveDockerContextDirectory(manifestDir, directory, wrapError, assetOutdir = manifestDir) {
|
|
8150
|
+
const resolved = resolveAssemblyPath(manifestDir, directory, { containWithin: assetOutdir });
|
|
8151
|
+
if (!resolved.contained) throw wrapError(`asset source.directory='${displaySafe(directory)}' which ${renderAssemblyPathEscape(resolved, assetOutdir, "build it")}`);
|
|
8152
|
+
return resolved.path;
|
|
8153
|
+
}
|
|
8154
|
+
/**
|
|
7737
8155
|
* Build a Docker image from a CDK asset source. Returns the local image
|
|
7738
8156
|
* tag the caller should use for `docker tag` / `docker push` (publisher)
|
|
7739
8157
|
* or `docker run` (local-invoke).
|
|
@@ -7751,10 +8169,11 @@ function describeDockerCapturedOutput(error, args, fallback) {
|
|
|
7751
8169
|
async function buildDockerImage(asset, cdkOutDir, options) {
|
|
7752
8170
|
const source = asset.source;
|
|
7753
8171
|
const logger = getLogger().child("docker-build");
|
|
8172
|
+
const contextDirectory = (directory) => resolveDockerContextDirectory(cdkOutDir, directory, options.wrapError, options.assetOutdir);
|
|
7754
8173
|
if (source.executable && source.executable.length > 0) {
|
|
7755
8174
|
const [cmd, ...args] = source.executable;
|
|
7756
8175
|
if (!cmd) throw options.wrapError("asset source.executable[] is empty");
|
|
7757
|
-
const cwd = source.directory ?
|
|
8176
|
+
const cwd = source.directory ? contextDirectory(source.directory) : cdkOutDir;
|
|
7758
8177
|
const shownExecutable = redactDockerArgvValues(source.executable).join(" ");
|
|
7759
8178
|
logger.debug(`Building Docker image via executable: ${shownExecutable} (cwd=${cwd})`);
|
|
7760
8179
|
let result;
|
|
@@ -7778,9 +8197,9 @@ async function buildDockerImage(asset, cdkOutDir, options) {
|
|
|
7778
8197
|
}
|
|
7779
8198
|
if (!options.tag) throw options.wrapError("buildDockerImage(directory mode) requires options.tag");
|
|
7780
8199
|
const buildArgs = buildDockerBuildCommand(source, options.tag, options.platform);
|
|
7781
|
-
const contextDir =
|
|
8200
|
+
const contextDir = contextDirectory(source.directory);
|
|
7782
8201
|
buildArgs.push(".");
|
|
7783
|
-
logger.debug(`${getDockerCmd()} ${redactDockerArgvValues(buildArgs).join(" ")} (cwd=${contextDir})`);
|
|
8202
|
+
logger.debug(`${getDockerCmd()} ${redactDockerArgvValues(buildArgs).join(" ")} (cwd=${displaySafe(contextDir)})`);
|
|
7784
8203
|
try {
|
|
7785
8204
|
await runDockerStreaming(buildArgs, {
|
|
7786
8205
|
cwd: contextDir,
|
|
@@ -7910,8 +8329,8 @@ var DockerAssetPublisher = class {
|
|
|
7910
8329
|
* via `docker tag` so the downstream `push()` step (which is wired to
|
|
7911
8330
|
* `localTag` at graph-construction time) keeps working unchanged.
|
|
7912
8331
|
*/
|
|
7913
|
-
async build(asset, cdkOutputDir, localTag) {
|
|
7914
|
-
await this.buildImage(asset, cdkOutputDir, localTag);
|
|
8332
|
+
async build(asset, cdkOutputDir, localTag, assetOutdir) {
|
|
8333
|
+
await this.buildImage(asset, cdkOutputDir, localTag, assetOutdir);
|
|
7915
8334
|
}
|
|
7916
8335
|
/**
|
|
7917
8336
|
* Push a pre-built Docker image to ECR (public, used by WorkGraph asset-publish nodes)
|
|
@@ -7967,9 +8386,10 @@ var DockerAssetPublisher = class {
|
|
|
7967
8386
|
* downstream push step finds the image under the deterministic
|
|
7968
8387
|
* `cdkd-asset-<hash>` name it expects.
|
|
7969
8388
|
*/
|
|
7970
|
-
async buildImage(asset, cdkOutputDir, tag) {
|
|
8389
|
+
async buildImage(asset, cdkOutputDir, tag, assetOutdir) {
|
|
7971
8390
|
const actualTag = await buildDockerImage(asset, cdkOutputDir, {
|
|
7972
8391
|
tag,
|
|
8392
|
+
...assetOutdir !== void 0 && { assetOutdir },
|
|
7973
8393
|
wrapError: (stderr) => new AssetError(`Docker build failed: ${stderr}`)
|
|
7974
8394
|
});
|
|
7975
8395
|
if (actualTag !== tag) {
|
|
@@ -9342,6 +9762,7 @@ var AssetPublisher = class {
|
|
|
9342
9762
|
const content = readFileSync(manifestPath, "utf-8");
|
|
9343
9763
|
const manifest = JSON.parse(content);
|
|
9344
9764
|
const cdkOutputDir = manifestPath.replace(/\/[^/]+$/, "");
|
|
9765
|
+
const assetOutdir = options.assetOutdir ?? cdkOutputDir;
|
|
9345
9766
|
const prefix = options.nodePrefix || "";
|
|
9346
9767
|
const redirect = options.redirect;
|
|
9347
9768
|
const nodeIds = [];
|
|
@@ -9358,6 +9779,7 @@ var AssetPublisher = class {
|
|
|
9358
9779
|
hash,
|
|
9359
9780
|
asset,
|
|
9360
9781
|
cdkOutputDir,
|
|
9782
|
+
assetOutdir,
|
|
9361
9783
|
accountId: options.accountId,
|
|
9362
9784
|
region: options.region,
|
|
9363
9785
|
...options.profile && { profile: options.profile }
|
|
@@ -9380,6 +9802,7 @@ var AssetPublisher = class {
|
|
|
9380
9802
|
hash,
|
|
9381
9803
|
asset,
|
|
9382
9804
|
cdkOutputDir,
|
|
9805
|
+
assetOutdir,
|
|
9383
9806
|
localTag
|
|
9384
9807
|
}
|
|
9385
9808
|
});
|
|
@@ -9406,8 +9829,8 @@ var AssetPublisher = class {
|
|
|
9406
9829
|
*/
|
|
9407
9830
|
async executeNode(node) {
|
|
9408
9831
|
const data = node.data;
|
|
9409
|
-
if (data.kind === "file") await this.filePublisher.publish(data.hash, data.asset, data.cdkOutputDir, data.accountId, data.region, data.profile);
|
|
9410
|
-
else if (data.kind === "docker-build") await this.dockerPublisher.build(data.asset, data.cdkOutputDir, data.localTag);
|
|
9832
|
+
if (data.kind === "file") await this.filePublisher.publish(data.hash, data.asset, data.cdkOutputDir, data.accountId, data.region, data.profile, data.assetOutdir);
|
|
9833
|
+
else if (data.kind === "docker-build") await this.dockerPublisher.build(data.asset, data.cdkOutputDir, data.localTag, data.assetOutdir);
|
|
9411
9834
|
else if (data.kind === "docker-publish") await this.dockerPublisher.push(data.asset, data.accountId, data.region, data.localTag);
|
|
9412
9835
|
this.logger.debug(`✅ ${node.id}`);
|
|
9413
9836
|
}
|
|
@@ -10162,85 +10585,6 @@ async function buildLockContentionMessage(args) {
|
|
|
10162
10585
|
return `${head} ${advice}, run: ${recoveryCommand}`;
|
|
10163
10586
|
}
|
|
10164
10587
|
|
|
10165
|
-
//#endregion
|
|
10166
|
-
//#region src/state/record-keys.ts
|
|
10167
|
-
/**
|
|
10168
|
-
* The ONE encoding anything that identifies a producer-side thing by a pair of
|
|
10169
|
-
* runtime strings goes through — a warned-once `Set`, a dedupe `Set`, a
|
|
10170
|
-
* memoization `Map`, a tree's node index.
|
|
10171
|
-
*
|
|
10172
|
-
* An import-free LEAF, the same shape as `src/utils/display-safe.ts` and
|
|
10173
|
-
* `src/utils/parameter-types.ts`, and that is load-bearing rather than tidy:
|
|
10174
|
-
* the consumers span the `cli`, `state` and `deployment` layers, and one of
|
|
10175
|
-
* them — `src/cli/commands/state-list-tree.ts` — is deliberately kept with NO
|
|
10176
|
-
* imports at all so the tree-building logic is unit-testable without mocking
|
|
10177
|
-
* anything. These functions previously lived in `malformed-resources-bag.ts`,
|
|
10178
|
-
* which reaches `error-handler` / `display-safe` / `lock-contention-message`,
|
|
10179
|
-
* and that module still RE-EXPORTS them so no existing importer moved.
|
|
10180
|
-
*
|
|
10181
|
-
* ENCODED, not separated. Issue
|
|
10182
|
-
* [#3308](https://github.com/go-to-k/cdkd/issues/3308) is the measurement
|
|
10183
|
-
* behind that and issue [#3323](https://github.com/go-to-k/cdkd/issues/3323)
|
|
10184
|
-
* is the one that found how far the class ran. Both halves of every key here
|
|
10185
|
-
* are attacker-influenced, and a separator only NARROWS a collision: with a
|
|
10186
|
-
* NUL separator, stack `Evil<NUL>us-east-1` in `ap-northeast-1` and stack
|
|
10187
|
-
* `Evil` in region `us-east-1<NUL>ap-northeast-1` produce the SAME key.
|
|
10188
|
-
*
|
|
10189
|
-
* `JSON.stringify` of a two-element array is injective over string pairs: the
|
|
10190
|
-
* quoting escapes anything that could imitate the separator, so no planted
|
|
10191
|
-
* value can produce another pair's key.
|
|
10192
|
-
*
|
|
10193
|
-
* **What a collision COSTS is per SITE, not a property of the key, and a note
|
|
10194
|
-
* here once claimed otherwise** — "one dropped warning LINE, never a wrong
|
|
10195
|
-
* resolution". That held for the two warned-once sets it was written for and
|
|
10196
|
-
* is false at three `cdkd scrub` sites, where a collision is a wrong ANSWER
|
|
10197
|
-
* feeding the pre-pass that decides whether a producer still holds plaintext.
|
|
10198
|
-
* Do not restore the narrower sentence: it reads as a licence to leave a
|
|
10199
|
-
* separator at a site nobody has re-checked.
|
|
10200
|
-
*
|
|
10201
|
-
* **Whether a separator was EVER injective is also per site**, because it
|
|
10202
|
-
* depends on each half's PROVENANCE. A half read out of an S3 key segment
|
|
10203
|
-
* cannot carry a NUL — measured on go-to-k/cdkd#3323, S3 refuses to store such
|
|
10204
|
-
* a key — so a key whose LEFT half is a segment splits uniquely at the FIRST
|
|
10205
|
-
* NUL. **The rule is two-sided and the dual is easy to miss**: a NUL-free
|
|
10206
|
-
* RIGHT half splits uniquely at the LAST one, which is what actually held at
|
|
10207
|
-
* one of the sites here. A half read out of a TEMPLATE or a state body is
|
|
10208
|
-
* gated by neither.
|
|
10209
|
-
*
|
|
10210
|
-
* Do not settle a site by asking whether the SEPARATOR is storable. That
|
|
10211
|
-
* question was asked twice on go-to-k/cdkd#3323 and gave the wrong answer both
|
|
10212
|
-
* times, in opposite directions. Encoding removes the need to ask at all,
|
|
10213
|
-
* which is why it is the fix rather than a wider separator.
|
|
10214
|
-
*/
|
|
10215
|
-
/** The ONE spelling of the encoding, private so no caller can re-derive it. */
|
|
10216
|
-
function injectivePairKey(first, second) {
|
|
10217
|
-
return JSON.stringify([first, second]);
|
|
10218
|
-
}
|
|
10219
|
-
/**
|
|
10220
|
-
* The key for a state RECORD: the `(stackName, region)` pair that names one
|
|
10221
|
-
* `state.json`.
|
|
10222
|
-
*/
|
|
10223
|
-
function producerRecordKey(stackName, region) {
|
|
10224
|
-
return injectivePairKey(stackName, region);
|
|
10225
|
-
}
|
|
10226
|
-
/**
|
|
10227
|
-
* The key for a producer COORDINATE: a `(stack, export-or-output name)` pair,
|
|
10228
|
-
* used by `cdkd scrub`'s re-export chain walk and its per-coordinate verdict
|
|
10229
|
-
* cache.
|
|
10230
|
-
*
|
|
10231
|
-
* A separate NAME because the subject is a different thing — a coordinate is
|
|
10232
|
-
* not a record, and a call site reading `producerRecordKey(stack, exportName)`
|
|
10233
|
-
* would say something false. ONE implementation, though, which is the property
|
|
10234
|
-
* the "one spelling" rule is actually about: two names over one implementation
|
|
10235
|
-
* cannot drift, two implementations can.
|
|
10236
|
-
*
|
|
10237
|
-
* An `exportName` is REDACTED on the way into state
|
|
10238
|
-
* (`StateImportEntry`'s JSDoc), so this half may hold any string at all.
|
|
10239
|
-
*/
|
|
10240
|
-
function producerCoordinateKey(stackName, exportOrOutputName) {
|
|
10241
|
-
return injectivePairKey(stackName, exportOrOutputName);
|
|
10242
|
-
}
|
|
10243
|
-
|
|
10244
10588
|
//#endregion
|
|
10245
10589
|
//#region src/utils/bucket-region-client.ts
|
|
10246
10590
|
/**
|
|
@@ -12875,10 +13219,17 @@ function recordedExpressionsOf(secrets) {
|
|
|
12875
13219
|
* the shape PR #2415 was forced to WITHDRAW (`provenPublicExpressions`,
|
|
12876
13220
|
* residual #2425): keyed on a value alone, one stack's answer is served to
|
|
12877
13221
|
* another stack's identically-spelled read. Here the key names the producer
|
|
12878
|
-
* stack, its region and the output — so a hit is served
|
|
12879
|
-
*
|
|
12880
|
-
*
|
|
12881
|
-
*
|
|
13222
|
+
* stack, its region and the output — so a hit is served to a resolution that
|
|
13223
|
+
* asked for that output of that stack, i.e. to the reader that would have
|
|
13224
|
+
* received the plaintext before this feature existed.
|
|
13225
|
+
*
|
|
13226
|
+
* `precisely`, in an earlier revision, overstated it. The key is
|
|
13227
|
+
* NUL-SEPARATED rather than encoded, and the halves are not all
|
|
13228
|
+
* charset-constrained, so a forged coordinate CAN collide with another's
|
|
13229
|
+
* key — see {@link maskedOutputKey} for why that is left standing and what
|
|
13230
|
+
* would change the answer (go-to-k/cdkd#3496). The bound that does hold is
|
|
13231
|
+
* the one above it: whoever can forge such a coordinate can already aim at
|
|
13232
|
+
* the real one, so nothing is widened by the collision either.
|
|
12882
13233
|
*
|
|
12883
13234
|
* A RECOVERED VALUE IS STILL SECRET, and every reader re-registers it as a
|
|
12884
13235
|
* mask-only needle in its OWN bag before using it — the recovery hands back the
|
|
@@ -12911,11 +13262,23 @@ const CONFLICTING_CROSS_STACK = Symbol("conflicting cross-stack association");
|
|
|
12911
13262
|
/**
|
|
12912
13263
|
* Separator for the composite keys {@link crossStackSourceKey} builds.
|
|
12913
13264
|
*
|
|
12914
|
-
* A NUL rather than a printable character
|
|
12915
|
-
*
|
|
12916
|
-
* source leaves can spell a single key. A printable separator (`:` / `|`) does
|
|
12917
|
-
* occur inside a real export name — CDK's own convention is
|
|
13265
|
+
* A NUL rather than a printable character: a printable separator (`:` / `|`)
|
|
13266
|
+
* does occur inside a real export name — CDK's own convention is
|
|
12918
13267
|
* `Stack:ExportName` — which would let one leaf's key be read as another's.
|
|
13268
|
+
*
|
|
13269
|
+
* **It does NOT follow that no two distinct source leaves can spell a single
|
|
13270
|
+
* key, and this note asserted that for a while** (go-to-k/cdkd#3496). The
|
|
13271
|
+
* reason given was that no AWS export name, stack name, output name, region or
|
|
13272
|
+
* role ARN can contain a NUL — a claim about what AWS ACCEPTS, while these
|
|
13273
|
+
* halves are TEMPLATE literals read straight out of the intrinsic. A hand-written
|
|
13274
|
+
* template can put a NUL in any of them.
|
|
13275
|
+
*
|
|
13276
|
+
* What makes the key safe is not injectivity but what happens on a collision:
|
|
13277
|
+
* the association store POISONS a slot recorded against a differing
|
|
13278
|
+
* (expression, plaintext) pair rather than overwriting it, and the scope is a
|
|
13279
|
+
* `WeakMap` on the pass's own bag. So a forged key degrades to a REFUSAL, never
|
|
13280
|
+
* to one leaf's expression being served for another's. Keep that property if
|
|
13281
|
+
* this separator is ever revisited; it, not the charset, is the guarantee.
|
|
12919
13282
|
*/
|
|
12920
13283
|
const CROSS_STACK_KEY_SEPARATOR = "\0";
|
|
12921
13284
|
/** A non-empty literal string, or `undefined` for anything else. */
|
|
@@ -17149,7 +17512,7 @@ function extractLambdaVpcDeleteDeps(resources) {
|
|
|
17149
17512
|
for (const targetId of targets) {
|
|
17150
17513
|
if (targetId === lambdaId) continue;
|
|
17151
17514
|
if (!(targetId in resources)) continue;
|
|
17152
|
-
const key =
|
|
17515
|
+
const key = injectiveKey(lambdaId, targetId);
|
|
17153
17516
|
if (seen.has(key)) continue;
|
|
17154
17517
|
seen.add(key);
|
|
17155
17518
|
edges.push({
|
|
@@ -27809,7 +28172,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
27809
28172
|
*/
|
|
27810
28173
|
cachedDynamicReferences = /* @__PURE__ */ new Map();
|
|
27811
28174
|
/**
|
|
27812
|
-
*
|
|
28175
|
+
* `(parameter name, reported Type)` pairs this resolver has already
|
|
27813
28176
|
* warned about (issue #1933 review).
|
|
27814
28177
|
*
|
|
27815
28178
|
* Keyed on the PAIR rather than the name alone: two different anomalous types
|
|
@@ -30427,7 +30790,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
30427
30790
|
* contract as {@link lookupCfnExport}.
|
|
30428
30791
|
*/
|
|
30429
30792
|
async lookupCfnStackOutputs(stackName, region, context, loggedRegionText) {
|
|
30430
|
-
const cacheKey =
|
|
30793
|
+
const cacheKey = injectiveKey(region, stackName);
|
|
30431
30794
|
let fetch = this.cfnStackOutputsCache.get(cacheKey);
|
|
30432
30795
|
if (!fetch) {
|
|
30433
30796
|
fetch = this.fetchCfnStackOutputs(stackName, region);
|
|
@@ -31926,8 +32289,8 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
31926
32289
|
if (paramValue === void 0 || paramValue === null) throw new Error(`Dynamic reference: SSM parameter '${loggedParameterName}' not found or has no value`);
|
|
31927
32290
|
const paramType = response.Parameter?.Type;
|
|
31928
32291
|
const secure = paramType !== "String" && paramType !== "StringList";
|
|
31929
|
-
if (secure && paramType !== "SecureString" && !this.warnedUnrecognizedSsmTypes.has(
|
|
31930
|
-
this.warnedUnrecognizedSsmTypes.add(
|
|
32292
|
+
if (secure && paramType !== "SecureString" && !this.warnedUnrecognizedSsmTypes.has(injectiveKey(parameterName, String(paramType)))) {
|
|
32293
|
+
this.warnedUnrecognizedSsmTypes.add(injectiveKey(parameterName, String(paramType)));
|
|
31931
32294
|
const reported = paramType === void 0 ? "(absent)" : `'${String(paramType)}'`;
|
|
31932
32295
|
this.logger.warn(`SSM parameter '${loggedParameterName}' reported an unrecognized Type ${reported} — treating its value as a secret, so cdkd will persist the {{resolve:${service}:...}} expression rather than the resolved value. Declare the parameter as String / StringList if it is public config.`);
|
|
31933
32296
|
}
|
|
@@ -33612,7 +33975,7 @@ var CloudControlProvider = class {
|
|
|
33612
33975
|
const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
|
|
33613
33976
|
if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
|
|
33614
33977
|
this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
|
|
33615
|
-
const { ASGProvider } = await import("./asg-provider-
|
|
33978
|
+
const { ASGProvider } = await import("./asg-provider-C9R-ND4b.js").then((n) => n.n);
|
|
33616
33979
|
const asgProvider = new ASGProvider();
|
|
33617
33980
|
return withIndeterminateGuard(await asgProvider.delete(logicalId, physicalId, resourceType, _properties, context), indeterminateGuard);
|
|
33618
33981
|
}
|
|
@@ -47320,5 +47683,5 @@ var DeployEngine = class DeployEngine {
|
|
|
47320
47683
|
};
|
|
47321
47684
|
|
|
47322
47685
|
//#endregion
|
|
47323
|
-
export { yellow as $,
|
|
47324
|
-
//# sourceMappingURL=deploy-engine-
|
|
47686
|
+
export { yellow as $, getAwsClients as $i, STATE_SOURCED_BASELINE_RULES as $n, validateContainerRepoName as $r, requireConfigObject as $t, createPreDeleteFinalSnapshot as A, isTransientServerError as Aa, ssmResolvedValueType as Ai, malformedResourcePropertiesWarning as An, hasUnverifiableParameterRefusal as Ar, carriesDynamicReference as At, isStatefulRecreateTargetSync as B, producerRecordKey as Bi, refuseMalformedState as Bn, buildAssetRedirectMap as Br, isSettledInstanceState as Bt, namesAnyDeclaredParameter as C, formatError as Ca, resolveCaptureObservedState as Ci, malformedExportSourceWarning as Cn, formatLockExpiry as Cr, slowCcOperationTimeoutMs as Ct, PRE_DELETE_SNAPSHOT_TYPES as D, isMarkedNonRetryable as Da, resolveUseCdkBootstrapAssets as Di, malformedOutputsWarning as Dn, exportNamesCarriedFrom as Dr, disableInstanceApiTermination as Dt, ATOMIC_FINAL_SNAPSHOT_TYPES as E, withErrorHandling as Ea, resolveStateBucketWithDefaultAndSource as Ei, malformedOutputsRefusalMessage as En, DEFAULT_STATE_PREFIX as Er, deleteSkipReason as Et, extractDeploymentEventError as F, safeStringify$1 as Fa, uploadCfnTemplate as Fi, refuseMalformedOutputs as Fn, orphansCarriedFrom as Fr, isImpossibleEmptyStoredAttribute as Ft, toCfnInteger as G, derivePartitionAndUrlSuffix as Gi, safeRegion as Gn, BOOTSTRAP_MARKER_PREFIX as Gr, coerceCfnBoolean as Gt, WARM_THROUGHPUT_MEMBERS as H, expectedOwnerParam as Hi, repairMalformedResourceEntriesForReadOnly as Hn, loadPublishableAssetManifest as Hr, normalizeAwsTagsToCfn as Ht, makeSiblingClaimReader as I, __exportAll as Ia, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as Ii, refuseMalformedOutputsForDestroy as In, shouldRetainResource as Ir, isNamelessDynamicReferenceError as It, bold as J, renderAssemblyPathEscape as Ji, findSilentDropProperties as Jn, getBootstrapMarkerKey as Jr, configIntegerRefusal as Jt, toFiniteNumber as K, AssemblyReader as Ki, safeStackName as Kn, assertAssetBucketRegion as Kr, coerceCfnInteger as Kt, planOrphanAdoption as L, injectiveKey as Li, refuseMalformedResourceEntries as Ln, AssetPublisher as Lr, isUnboundTemplateParameter as Lt, refusesFinalSnapshot as M, markRedactedCause as Ma, CFN_TEMPLATE_URL_LIMIT as Mi, malformedStateRefusalMessage as Mn, importableOutputs as Mr, cfnRefValueFromPhysicalId as Mt, unsupportedFinalSnapshotError as N, retryClassificationText as Na, MIGRATE_TMP_PREFIX as Ni, refuseDivergentRecordRegionForDestroy as Nn, isReadableBag as Nr, coerceParameterTypedValue as Nt, buildFinalSnapshotIdentifier as O, isRetryableTransientError as Oa, stateBucketExistenceConfirmed as Oi, malformedRenderedContainersWarning as On, hasReadableExportSet as Or, isTerminationProtectionPropagationError as Ot, makeCanonicalizePropertiesFn as P, describeAwsFailure as Pa, findLargeInlineResources as Pi, refuseMalformedNestedChildOutputs as Pn, orphansAfterRollback as Pr, getAccountInfo as Pt, red as Q, AwsClients as Qi, TemplateParser as Qn, validateAssetBucketName as Qr, requireConfigArray as Qt, MULTI_REGION_RECREATE_BLOCKED_TYPES as R, injectiveKeyPrefix as Ri, refuseMalformedResourcePropertiesForOrphan as Rn, stringifyValue as Rr, parameterTypeMayLoseSecretIdentity as Rt, computeParameterTaint as S, SynthesisError as Sa, resolveAutoAssetStorage as Si, malformedExportNamesWarning as Sn, forceQuitRecoveryClause as Sr, isWaitAbandonedError as St, resourcesNamingDeclaredParameter as T, normalizeAwsError as Ta, resolveStateBucketWithDefault as Ti, malformedOrphanRecordsWarning as Tn, CUSTOM_RESOURCE_RESPONSE_PREFIX as Tr, deleteIndeterminateGuards as Tt, coerceWarmThroughput as U, PARTITION_TABLE as Ui, repairMalformedResourcePropertiesForReadOnly as Un, rewriteTemplateAssetReferences as Ur, resolveExplicitPhysicalId as Ut, renderStatefulReason as V, decodeListingKey as Vi, repairMalformedOutputsForReadOnly as Vn, createAssetRedirectResolver as Vr, WAFv2WebACLProvider as Vt, isWarmThroughputDecrease as W, canonicalizeRegion as Wi, repairMalformedResourcesForReadOnly as Wn, AssetModeResolver as Wr, assertRegionMatch as Wt, gray as X, clearBucketRegionCache as Xi, withRetry as Xn, parseBootstrapMarker as Xr, readConfigString as Xt, cyan as Y, resolveAssemblyPath as Yi, describeTypeWithThrottleRetry as Yn, isCrossRegionRedirect as Yr, configStringRefusal as Yt, green as Z, resolveBucketRegion as Zi, DagBuilder as Zn, readBootstrapMarkerBody as Zr, replayWarn as Zt, bagHoldsSecretExpression as _, ResourceTimeoutError as _a, Synthesizer as _i, UNREADABLE_RESOURCES_MAP_ROW as _n, S3StateBackend as _r, endCommandInterruptScope as _t, DeploymentEventsStore as a, CrossAccountSecretRefusalError as aa, describeDockerFailure as ai, s3BucketDomainName as an, dynamicReferenceTokens as ar, secretBearingStateKeyWarning as at, mergeNoChangeOutputs as b, StackTerminationProtectionError as ba, getLegacyStateBucketName as bi, hasReadableResources as bn, buildForceUnlockCommand as br, startInterruptWatch as bt, replayFailedOperations as c, DynamicReferenceRegionAmbiguousError as ca, getDockerCmd as ci, s3BucketWebsiteUrl as cn, isSingleDynamicReferenceToken as cr, IAMRoleProvider as ct, updatePartialMessage as d, LocalStartServiceError as da, runDockerForeground as di, applyCallerIdentityCredentials as dn, maskSecretsInText as dr, ProviderRegistry as dt, resetAwsClients as ea, buildDenyExternalAccessPolicy as ei, requireConfigString as en, STATE_SOURCED_CROSS_GENERATION_RULES as er, collectDeclaredOutputNames as et, updatePartialReason as f, LockError as fa, runDockerStreaming as fi, callerEnvCredentials as fn, recordMaskOnlyValue as fr, wouldReturnToSdkProvider as ft, computeImplicitDeleteEdges as g, ProvisioningError as ga, getDockerImageBySourceHash as gi, STATE_RESOURCES_MALFORMED as gn, LockManager as gr, beginCommandInterruptScope as gt, IMPLICIT_DELETE_DEPENDENCIES as h, PartialFailureError as ha, AssetManifestLoader as hi, INTRINSIC_KEYS as hn, scrubResourceRecord as hr, maskerOrIdentity as ht, DeploymentEventsReader as i, ConfigError as ia, describeDockerExecFailure as ii, s3BucketArn as in, createSecretMasker as ir, secretBearing as it, isFinalSnapshotError as j, markNonRetryable as ja, CFN_TEMPLATE_BODY_LIMIT as ji, malformedResourcesWarning as jn, importableOutputKeys as jr, carriesFetchableDynamicReference as jt, ccRoutedFinalSnapshotError as k, isThrottlingError as ka, warnDeprecatedNoPrefixCliFlag as ki, malformedResourceEntriesWarning as kn, hasReasonlessBaselineRefusal as kr, IntrinsicFunctionResolver as kt, replayRollback as l, IntrinsicResolutionRefusalError as la, partitionSensitiveEnv as li, applyRoleArnIfSet as ln, markSameGenerationBag as lr, collectInlinePolicyNamesManagedBySiblings as lt, maskingRetryLogger as m, NestedStackChildDirectDestroyError as ma, stripControlChars as mi, DiffCalculator as mn, redactSecretsForState as mr, maskDeep as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, AssetError as na, resolveDockerContextDirectory as ni, producerRegionsFromState as nn, TEMPLATE_SOURCED_RULES as nr, exportAliasCollisionScrubWarning as nt, planFailedOps as o, DependencyError as oa, dockerSpawnEnvWithSensitive as oi, s3BucketDualStackDomainName as on, errorCauseChain as or, secretSafeKeyDisplay as ot, withResourceDeadline as p, MalformedProducerRecordRefusalError as pa, escapeRegExp$1 as pi, envCredentialsAreAssumedRole as pn, recoverMaskedOutput as pr, createMaskedRetryLogger as pt, formatResourceLine as q, processStackMessages as qi, findActionableSilentDrops as qn, ensureAssetStorage as qr, configBooleanRefusal as qt, DeployEngine as r, CdkdError as ra, describeDockerCapturedOutput as ri, withSharedDrainBudget as rn, carriesSecretMask as rr, isExportAliasCollision as rt, planRollback as s, DeployCancelledError as sa, formatDockerLoginError as si, s3BucketRegionalDomainName as sn, identityKeyFor as sr, getCurrentResourceSecrets as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, setAwsClients as ta, buildDockerImage as ti, classifyReplaySecretRegion as tn, STATE_SOURCED_READBACK_RULES as tr, collectPublishedOutputNames as tt, resolveReplacementOldType as u, LocalInvokeBuildError as ua, redactDockerArgvValues as ui, AWS_CREDENTIAL_ENV_KEYS as un, maskSecretsInError as ur, clearOnUpdateRemoval as ut, isSecretBearingReferenceString as v, ResourceUpdateNotSupportedError as va, synthesisStatusMessage as vi, displayLogicalId as vn, rebuildClientForBucketRegion as vr, interruptWatchListenerCount as vt, reachableDivergentParameters as w, isCdkdError as wa, resolveSkipPrefix as wi, malformedLocalOutputsWarning as wn, shellQuote as wr, UNSPECIFIED_SKIP_REASON as wt, bindingSkippedOutputs as x, StateError as xa, resolveApp as xi, isReadableResourceEntry as xn, buildLockContentionMessage as xr, CloudControlProvider as xt, keptWholeReasonText as y, StackHasActiveImportsError as ya, getDefaultStateBucketName as yi, hasReadableOutputs as yn, UNREPRODUCIBLE_LOCK_CLAUSE as yr, isInterruptedWaitError as yt, isStatefulRecreateTargetForReplace as z, producerCoordinateKey as zi, refuseMalformedResourcesForDestroy as zn, WorkGraph as zr, refStateLookupFromResource as zt };
|
|
47687
|
+
//# sourceMappingURL=deploy-engine-Bw8VoyEi.js.map
|