From 8fb17e8ea49dd19ecb9e50b4cc22bbed184e8137 Mon Sep 17 00:00:00 2001 From: Waldemar Hummer Date: Sat, 28 Mar 2026 11:15:00 -0700 Subject: [PATCH 1/3] fix: apply forcePathStyle patch only for sandbox endpoints or when AWS_S3_FORCE_PATH_STYLE is set Co-Authored-By: Claude Sonnet 4.6 --- bin/cdklocal | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/bin/cdklocal b/bin/cdklocal index 415998e..d8a3336 100755 --- a/bin/cdklocal +++ b/bin/cdklocal @@ -115,6 +115,18 @@ const useLocal = () => { return true; }; +const shouldForcePathStyle = () => { + if (isEnvTrue(process.env.AWS_S3_FORCE_PATH_STYLE)) { + return true; + } + const endpointUrl = process.env.AWS_ENDPOINT_URL || ""; + try { + return new URL(endpointUrl).hostname.includes(".sandbox."); + } catch { + return false; + } +}; + const md5 = s => crypto.createHash("md5").update(s).digest("hex"); const getMethods = (obj) => { @@ -485,6 +497,30 @@ const patchPost_2_14 = () => { case "ERR_PACKAGE_PATH_NOT_EXPORTED": // post 2.177 configureEnvironment(process.env, AWS_ENVAR_ALLOWLIST); + // require("aws-cdk/lib") resolves to legacy-exports.js which re-exports SdkProvider + // as a thin wrapper with only static methods. The real SdkProvider (with _makeSdk + // and other instance methods) lives in index.js. Load index.js directly by resolving + // the package root and bypassing the exports field, then patch _makeSdk to inject + // forcePathStyle:true into every SDK instance's config so S3Client uses path-style URLs. + // Required for remote LocalStack endpoints (e.g. *.sandbox.localstack.cloud) where + // the wildcard TLS cert only covers one subdomain level. + try { + const cdkRoot = path.dirname(require.resolve("aws-cdk/package.json")); + const indexLib = require(path.join(cdkRoot, "lib", "index.js")); + const RealSdkProvider = indexLib.SdkProvider; + if (RealSdkProvider && typeof RealSdkProvider.prototype._makeSdk === "function") { + const origMakeSdk = RealSdkProvider.prototype._makeSdk; + RealSdkProvider.prototype._makeSdk = function patchedMakeSdk(...args) { + const sdk = origMakeSdk.apply(this, args); + if (sdk && sdk.config && shouldForcePathStyle()) { + sdk.config.forcePathStyle = true; + } + return sdk; + }; + } + } catch (patchErr) { + // swallow - patch is best-effort for endpoints that don't need path-style + } break; default: // a different error From 5f204a5a49ddb28c24f6c024beddd69f246c5b85 Mon Sep 17 00:00:00 2001 From: Waldemar Hummer Date: Sat, 28 Mar 2026 11:23:48 -0700 Subject: [PATCH 2/3] chore: bump version to 3.0.4 and add changelog entry Co-Authored-By: Claude Sonnet 4.6 --- README.md | 1 + package-lock.json | 16 ++++++++-------- package.json | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 2b9d634..32d1e1e 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,7 @@ $ awslocal sns list-topics ## Change Log +* 3.0.4: Fix asset publishing failures for CDK >= 2.177.0 against remote LocalStack endpoints (e.g. `*.sandbox.localstack.cloud`) * 3.0.3: Add missing `semver` dependency * 3.0.2: Add support for `aws-cdk` versions after 2026-03-01 (see https://github.com/aws/aws-cdk-cli/issues/310) * 3.0.1: Using the `-v`/`--version` flag prints the cdklocal version and the version of the underlying `aws-cdk` package (if possible) diff --git a/package-lock.json b/package-lock.json index 0723132..886d57e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "aws-cdk-local", - "version": "3.0.3", + "version": "3.0.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "aws-cdk-local", - "version": "3.0.3", + "version": "3.0.4", "license": "Apache-2.0", "dependencies": { "diff": "^5.0.0", @@ -1087,7 +1087,7 @@ "license": "MIT" }, "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", + "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", "dev": true, @@ -1408,7 +1408,7 @@ } }, "node_modules/braces": { - "version": "3.0.3", + "version": "3.0.4", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, @@ -3189,7 +3189,7 @@ } }, "node_modules/kleur": { - "version": "3.0.3", + "version": "3.0.4", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true, @@ -5013,7 +5013,7 @@ "dev": true }, "@types/istanbul-lib-report": { - "version": "3.0.3", + "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", "dev": true, @@ -5250,7 +5250,7 @@ } }, "braces": { - "version": "3.0.3", + "version": "3.0.4", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, @@ -6486,7 +6486,7 @@ "dev": true }, "kleur": { - "version": "3.0.3", + "version": "3.0.4", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true diff --git a/package.json b/package.json index 6ec0c4b..7d39cc4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "aws-cdk-local", "description": "CDK Toolkit for use with LocalStack", - "version": "3.0.3", + "version": "3.0.4", "main": "src/index.js", "bin": { "cdklocal": "bin/cdklocal" From 64c955454ebb92a25223dcdce63243d49de1a6a5 Mon Sep 17 00:00:00 2001 From: Waldemar Hummer Date: Sat, 28 Mar 2026 11:26:55 -0700 Subject: [PATCH 3/3] refactor: extract loadRealSdkProvider helper and improve comments Co-Authored-By: Claude Sonnet 4.6 --- bin/cdklocal | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/bin/cdklocal b/bin/cdklocal index d8a3336..9bdd05a 100755 --- a/bin/cdklocal +++ b/bin/cdklocal @@ -115,6 +115,16 @@ const useLocal = () => { return true; }; +// require("aws-cdk/lib") resolves to legacy-exports.js which re-exports SdkProvider as a thin +// wrapper with only static methods. The real SdkProvider (with _makeSdk and other instance +// methods) lives in index.js. Load it directly by resolving the package root and bypassing +// the exports field. +const loadRealSdkProvider = () => { + const cdkRoot = path.dirname(require.resolve("aws-cdk/package.json")); + const indexLib = require(path.join(cdkRoot, "lib", "index.js")); + return indexLib.SdkProvider; +}; + const shouldForcePathStyle = () => { if (isEnvTrue(process.env.AWS_S3_FORCE_PATH_STYLE)) { return true; @@ -497,21 +507,16 @@ const patchPost_2_14 = () => { case "ERR_PACKAGE_PATH_NOT_EXPORTED": // post 2.177 configureEnvironment(process.env, AWS_ENVAR_ALLOWLIST); - // require("aws-cdk/lib") resolves to legacy-exports.js which re-exports SdkProvider - // as a thin wrapper with only static methods. The real SdkProvider (with _makeSdk - // and other instance methods) lives in index.js. Load index.js directly by resolving - // the package root and bypassing the exports field, then patch _makeSdk to inject - // forcePathStyle:true into every SDK instance's config so S3Client uses path-style URLs. - // Required for remote LocalStack endpoints (e.g. *.sandbox.localstack.cloud) where - // the wildcard TLS cert only covers one subdomain level. try { - const cdkRoot = path.dirname(require.resolve("aws-cdk/package.json")); - const indexLib = require(path.join(cdkRoot, "lib", "index.js")); - const RealSdkProvider = indexLib.SdkProvider; + const RealSdkProvider = loadRealSdkProvider(); if (RealSdkProvider && typeof RealSdkProvider.prototype._makeSdk === "function") { const origMakeSdk = RealSdkProvider.prototype._makeSdk; RealSdkProvider.prototype._makeSdk = function patchedMakeSdk(...args) { const sdk = origMakeSdk.apply(this, args); + // S3 assets are uploaded using path-style URLs (e.g. https:////...) + // rather than virtual-hosted-style (e.g. https://./...). + // This is required for sandbox endpoints whose wildcard TLS cert only covers + // one subdomain level, and can also be opted into via AWS_S3_FORCE_PATH_STYLE. if (sdk && sdk.config && shouldForcePathStyle()) { sdk.config.forcePathStyle = true; }