cdk-local 0.3.0 → 0.4.0
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/README.md +20 -1
- package/dist/cli.js +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/{local-start-service-C3Y-nNM3.js → local-start-service-zoDos4zT.js} +127 -12
- package/dist/{local-start-service-C3Y-nNM3.js.map → local-start-service-zoDos4zT.js.map} +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Local runner for your CDK app's Lambda functions, API Gateway, and ECS tasks/services. Run it with no AWS account, or bind it to your deployed stack to hit real AWS resources and data. A native, CDK-first alternative to `sam local`.
|
|
4
4
|
|
|
5
|
-

|
|
6
6
|
|
|
7
7
|
## Why cdk-local
|
|
8
8
|
|
|
@@ -64,6 +64,8 @@ Invoke a single Lambda function with an event payload.
|
|
|
64
64
|
cdkl invoke MyStack/MyFunction --event ./event.json
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
+

|
|
68
|
+
|
|
67
69
|
#### API Gateway — `start-api`
|
|
68
70
|
|
|
69
71
|
Serve your API Gateway routes (REST v1 / HTTP v2 / Function URL / WebSocket) on a local HTTP server.
|
|
@@ -81,6 +83,8 @@ cdkl run-task MyStack/MyTask
|
|
|
81
83
|
cdkl start-service MyStack/MyService
|
|
82
84
|
```
|
|
83
85
|
|
|
86
|
+
There is no cluster command — locally, Docker is the placement target a cluster abstracts away, so there is nothing to run. Both commands accept an optional `--cluster <name>` to set the cluster name surfaced to `ECS_CONTAINER_METADATA_URI_V4` (also used as the local Docker network prefix). See [docs/cli-reference.md](docs/cli-reference.md) for the full ECS option list.
|
|
87
|
+
|
|
84
88
|
Use this for fast iteration on Lambda code, API routing checks, and container task smoke tests.
|
|
85
89
|
|
|
86
90
|
### 2. Bound to a deployed stack
|
|
@@ -119,6 +123,21 @@ cdkl start-service MyStack/MyService --from-cfn-stack MyStack
|
|
|
119
123
|
|
|
120
124
|
Use this for production debugging, integration verification with real AWS resources, and validating real IAM permissions before deploy.
|
|
121
125
|
|
|
126
|
+
## `--watch` (hot reload)
|
|
127
|
+
|
|
128
|
+
Pass `--watch` to `cdkl start-api` and the server hot-reloads when your CDK app's synth output (or any routed Lambda's asset directory) changes:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
cdkl start-api MyStack/MyApi --watch
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
- 500 ms debounced [chokidar](https://github.com/paulmillr/chokidar) file watcher on `cdk.out/` + every routed Lambda's asset dir.
|
|
135
|
+
- Re-synths and re-discovers routes on each firing — adding a new route to your CDK app shows up locally on save.
|
|
136
|
+
- Synth failures keep the previous version serving (warn-and-continue, never crashes the server).
|
|
137
|
+
- Compatible with `--from-cfn-stack`: each reload re-reads the deployed CloudFormation stack so a deploy event picks up new ARNs without restarting the server.
|
|
138
|
+
|
|
139
|
+
See [docs/local-emulation.md](docs/local-emulation.md#hot-reload---watch) for the full lifecycle, file-list update semantics, and known limitations.
|
|
140
|
+
|
|
122
141
|
## Override env vars without a state source
|
|
123
142
|
|
|
124
143
|
When env-var values in your CDK template are CloudFormation intrinsics (`Ref`, `Fn::GetAtt`, `Fn::ImportValue`), cdk-local cannot resolve them without a state source — it drops them with a warning that names the affected key. To inject literal values instead, use `--env-vars <file>` (SAM-compatible JSON shape):
|
package/dist/cli.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { i as createLocalInvokeCommand, n as createLocalRunTaskCommand, r as createLocalStartApiCommand, t as createLocalStartServiceCommand } from "./local-start-service-
|
|
2
|
+
import { i as createLocalInvokeCommand, n as createLocalRunTaskCommand, r as createLocalStartApiCommand, t as createLocalStartServiceCommand } from "./local-start-service-zoDos4zT.js";
|
|
3
3
|
import { Command } from "commander";
|
|
4
4
|
|
|
5
5
|
//#region src/cli/index.ts
|
|
6
6
|
const program = new Command();
|
|
7
|
-
program.name("cdkl").description("Run AWS CDK stacks locally with Docker.").version("0.
|
|
7
|
+
program.name("cdkl").description("Run AWS CDK stacks locally with Docker.").version("0.4.0");
|
|
8
8
|
program.addCommand(createLocalInvokeCommand());
|
|
9
9
|
program.addCommand(createLocalStartApiCommand());
|
|
10
10
|
program.addCommand(createLocalRunTaskCommand());
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/types/state.ts","../src/local/state-resolver.ts","../src/local/local-state-provider.ts","../src/cli/commands/local-state-source.ts","../src/cli/commands/local-invoke.ts","../src/cli/commands/local-start-api.ts","../src/cli/commands/local-run-task.ts","../src/cli/commands/local-start-service.ts","../src/local/cfn-local-state-provider.ts"],"mappings":";;;;UAuLiB,aAAA;EAEf,UAAA;EAGA,YAAA;EAGA,UAAA,EAAY,MAAA;EAcZ,kBAAA,GAAqB,MAAA;EAGrB,UAAA,GAAa,MAAA;EAGb,YAAA;EAGA,QAAA,GAAW,MAAA;EAmBX,cAAA;EAMA,mBAAA;EAyBA,aAAA;AAAA;;;UCxIe,gBAAA;EACf,SAAA;EACA,MAAA;EACA,SAAA;EACA,SAAA;AAAA;AAAA,UAqBe,kBAAA;EAOf,aAAA,CAAc,UAAA,WAAqB,OAAA;EAQnC,qBAAA,CACE,aAAA,UACA,cAAA,UACA,UAAA,WACC,OAAA;AAAA;AAAA,UAGY,mBAAA;EAEf,SAAA,EAAW,MAAA,SAAe,aAAA;EAE1B,gBAAA,GAAmB,gBAAA;EAOnB,kBAAA,GAAqB,kBAAA;EAOrB,cAAA;AAAA;;;UCrJe,gBAAA;EAQf,SAAA,EAAW,MAAA,SAAe,aAAA;EAO1B,OAAA,EAAS,MAAA;EAOT,MAAA;AAAA;AAAA,UAsBe,kBAAA;EAAA,SAMN,KAAA;EAQT,IAAA,CAAK,SAAA,UAAmB,WAAA,uBAAkC,OAAA,CAAQ,gBAAA;EAalE,uBAAA,CAAwB,cAAA,WAAyB,OAAA,CAAQ,kBAAA;EAKzD,OAAA;AAAA;;;UC/Ee,uBAAA;EAOf,YAAA;EAEA,MAAA;EAEA,OAAA;EAMA,WAAA;EAAA,CAEC,GAAA;AAAA;AAAA,KAQS,yBAAA,IAA6B,OAAA,EAAS,uBAAA,KAA4B,kBAAA;AAAA,KAUlE,mBAAA,GAAsB,MAAA,SAAe,yBAAA;AAAA,iBAUjC,mBAAA,CAAoB,YAAA,oBAAgC,SAAA;AAAA,iBAapD,gBAAA,CAAiB,IAAA,EAAM,IAAA,CAAK,uBAAA;AAAA,iBAgB5B,gBAAA,CACd,OAAA,EAAS,IAAA,CAAK,uBAAA,6BACd,WAAA;AAAA,cAqBW,qBAAA,SAA8B,KAAA;cAC7B,OAAA;AAAA;AAAA,iBAkBE,wCAAA,CACd,OAAA,EAAS,IAAA,CAAK,uBAAA,mBACd,gBAAA;AAAA,iBAiCc,wBAAA,CACd,OAAA,EAAS,uBAAA,EACT,SAAA,UACA,WAAA,sBACA,mBAAA,GAAsB,mBAAA,GACrB,kBAAA;;;UCtEc,+BAAA;EACf,mBAAA,GAAsB,mBAAA;AAAA;AAAA,iBAu2BR,wBAAA,CAAyB,IAAA,GAAM,+BAAA,GAAuC,OAAA;;;UC5vBrE,iCAAA;EACf,mBAAA,GAAsB,mBAAA;AAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/types/state.ts","../src/local/state-resolver.ts","../src/local/local-state-provider.ts","../src/cli/commands/local-state-source.ts","../src/cli/commands/local-invoke.ts","../src/cli/commands/local-start-api.ts","../src/cli/commands/local-run-task.ts","../src/cli/commands/local-start-service.ts","../src/local/cfn-local-state-provider.ts"],"mappings":";;;;UAuLiB,aAAA;EAEf,UAAA;EAGA,YAAA;EAGA,UAAA,EAAY,MAAA;EAcZ,kBAAA,GAAqB,MAAA;EAGrB,UAAA,GAAa,MAAA;EAGb,YAAA;EAGA,QAAA,GAAW,MAAA;EAmBX,cAAA;EAMA,mBAAA;EAyBA,aAAA;AAAA;;;UCxIe,gBAAA;EACf,SAAA;EACA,MAAA;EACA,SAAA;EACA,SAAA;AAAA;AAAA,UAqBe,kBAAA;EAOf,aAAA,CAAc,UAAA,WAAqB,OAAA;EAQnC,qBAAA,CACE,aAAA,UACA,cAAA,UACA,UAAA,WACC,OAAA;AAAA;AAAA,UAGY,mBAAA;EAEf,SAAA,EAAW,MAAA,SAAe,aAAA;EAE1B,gBAAA,GAAmB,gBAAA;EAOnB,kBAAA,GAAqB,kBAAA;EAOrB,cAAA;AAAA;;;UCrJe,gBAAA;EAQf,SAAA,EAAW,MAAA,SAAe,aAAA;EAO1B,OAAA,EAAS,MAAA;EAOT,MAAA;AAAA;AAAA,UAsBe,kBAAA;EAAA,SAMN,KAAA;EAQT,IAAA,CAAK,SAAA,UAAmB,WAAA,uBAAkC,OAAA,CAAQ,gBAAA;EAalE,uBAAA,CAAwB,cAAA,WAAyB,OAAA,CAAQ,kBAAA;EAKzD,OAAA;AAAA;;;UC/Ee,uBAAA;EAOf,YAAA;EAEA,MAAA;EAEA,OAAA;EAMA,WAAA;EAAA,CAEC,GAAA;AAAA;AAAA,KAQS,yBAAA,IAA6B,OAAA,EAAS,uBAAA,KAA4B,kBAAA;AAAA,KAUlE,mBAAA,GAAsB,MAAA,SAAe,yBAAA;AAAA,iBAUjC,mBAAA,CAAoB,YAAA,oBAAgC,SAAA;AAAA,iBAapD,gBAAA,CAAiB,IAAA,EAAM,IAAA,CAAK,uBAAA;AAAA,iBAgB5B,gBAAA,CACd,OAAA,EAAS,IAAA,CAAK,uBAAA,6BACd,WAAA;AAAA,cAqBW,qBAAA,SAA8B,KAAA;cAC7B,OAAA;AAAA;AAAA,iBAkBE,wCAAA,CACd,OAAA,EAAS,IAAA,CAAK,uBAAA,mBACd,gBAAA;AAAA,iBAiCc,wBAAA,CACd,OAAA,EAAS,uBAAA,EACT,SAAA,UACA,WAAA,sBACA,mBAAA,GAAsB,mBAAA,GACrB,kBAAA;;;UCtEc,+BAAA;EACf,mBAAA,GAAsB,mBAAA;AAAA;AAAA,iBAu2BR,wBAAA,CAAyB,IAAA,GAAM,+BAAA,GAAuC,OAAA;;;UC5vBrE,iCAAA;EACf,mBAAA,GAAsB,mBAAA;AAAA;AAAA,iBA+xFR,0BAAA,CAA2B,IAAA,GAAM,iCAAA,GAAyC,OAAA;;;UCh7FzE,gCAAA;EACf,mBAAA,GAAsB,mBAAA;AAAA;AAAA,iBAkeR,yBAAA,CAA0B,IAAA,GAAM,gCAAA,GAAwC,OAAA;;;UCtevE,qCAAA;EACf,mBAAA,GAAsB,mBAAA;AAAA;AAAA,iBA+oBR,8BAAA,CACd,IAAA,GAAM,qCAAA,GACL,OAAA;;;UC5qBc,4BAAA;EAOf,YAAA;EAKA,MAAA;EAiBA,OAAA;AAAA;AAAA,cAGW,qBAAA,YAAiC,kBAAA;EAAA,SAG5B,KAAA;EAAA,iBACC,YAAA;EAAA,iBACA,MAAA;EAAA,QAKT,MAAA;EAAA,iBACS,aAAA;EAAA,QAQT,QAAA;cAEI,IAAA,EAAM,4BAAA;EAAA,QAOV,SAAA;EA+BK,IAAA,CACX,UAAA,UACA,YAAA,uBACC,OAAA,CAAQ,gBAAA;EA8DE,uBAAA,CACX,eAAA,WACC,OAAA,CAAQ,kBAAA;EAiEJ,OAAA,CAAA;AAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as LocalStateSourceError, c as rejectExplicitCfnStackWithMultipleStacks, d as CfnLocalStateProvider, i as createLocalInvokeCommand, l as resolveCfnRegion, n as createLocalRunTaskCommand, o as createLocalStateProvider, r as createLocalStartApiCommand, s as isCfnFlagPresent, t as createLocalStartServiceCommand, u as resolveCfnStackName } from "./local-start-service-
|
|
1
|
+
import { a as LocalStateSourceError, c as rejectExplicitCfnStackWithMultipleStacks, d as CfnLocalStateProvider, i as createLocalInvokeCommand, l as resolveCfnRegion, n as createLocalRunTaskCommand, o as createLocalStateProvider, r as createLocalStartApiCommand, s as isCfnFlagPresent, t as createLocalStartServiceCommand, u as resolveCfnStackName } from "./local-start-service-zoDos4zT.js";
|
|
2
2
|
|
|
3
3
|
export { CfnLocalStateProvider, LocalStateSourceError, createLocalInvokeCommand, createLocalRunTaskCommand, createLocalStartApiCommand, createLocalStartServiceCommand, createLocalStateProvider, isCfnFlagPresent, rejectExplicitCfnStackWithMultipleStacks, resolveCfnRegion, resolveCfnStackName };
|
|
@@ -10515,6 +10515,72 @@ function buildCorsConfigFromCloudFrontChain(template) {
|
|
|
10515
10515
|
return out;
|
|
10516
10516
|
}
|
|
10517
10517
|
/**
|
|
10518
|
+
* Determine whether a Function URL (`AWS::Lambda::Url`, identified by its
|
|
10519
|
+
* logical id) is fronted by a CloudFront Distribution origin that uses
|
|
10520
|
+
* Origin Access Control (OAC) to SIGN origin requests.
|
|
10521
|
+
*
|
|
10522
|
+
* Production-correct CDK pattern (`FunctionUrlOrigin.withOriginAccessControl`):
|
|
10523
|
+
* the Function URL declares `AuthType: AWS_IAM`, but the END client never
|
|
10524
|
+
* signs as the IAM principal — CloudFront re-signs the origin request with
|
|
10525
|
+
* its own SigV4 credentials (service `lambda`) via the OAC, and the Function
|
|
10526
|
+
* URL's auto-generated resource policy trusts `cloudfront.amazonaws.com`.
|
|
10527
|
+
* Locally there is no CloudFront in the path, so no client signature can
|
|
10528
|
+
* reproduce CloudFront's. Callers use this to relax SigV4 verification
|
|
10529
|
+
* (warn-and-pass) for these Function URLs without forcing
|
|
10530
|
+
* `--allow-unverified-sigv4`.
|
|
10531
|
+
*
|
|
10532
|
+
* Detection: a CloudFront origin whose `DomainName` matches the canonical
|
|
10533
|
+
* `Fn::GetAtt[<fnUrlLogicalId>, 'FunctionUrl']` chain (see
|
|
10534
|
+
* {@link pickFnUrlLogicalIdFromOriginDomainName}) AND carries an
|
|
10535
|
+
* `OriginAccessControlId`. When that id resolves to an
|
|
10536
|
+
* `AWS::CloudFront::OriginAccessControl` whose `SigningBehavior` is
|
|
10537
|
+
* explicitly `never`, CloudFront does NOT sign — so we do NOT relax (the
|
|
10538
|
+
* AWS_IAM + never-sign combination is non-functional in production too).
|
|
10539
|
+
* Any other signing behavior (`always` — the CDK default — or `no-override`)
|
|
10540
|
+
* counts as OAC-fronted. An `OriginAccessControlId` that can't be resolved
|
|
10541
|
+
* to a local resource (imported literal id) also counts — its presence on a
|
|
10542
|
+
* Function URL origin is the signal.
|
|
10543
|
+
*/
|
|
10544
|
+
function isFunctionUrlOacFronted(template, fnUrlLogicalId) {
|
|
10545
|
+
const resources = template.Resources ?? {};
|
|
10546
|
+
for (const [, resource] of Object.entries(resources)) {
|
|
10547
|
+
if (resource.Type !== "AWS::CloudFront::Distribution") continue;
|
|
10548
|
+
const distConfig = (resource.Properties ?? {})["DistributionConfig"];
|
|
10549
|
+
if (!distConfig || typeof distConfig !== "object") continue;
|
|
10550
|
+
const origins = Array.isArray(distConfig["Origins"]) ? distConfig["Origins"] : [];
|
|
10551
|
+
for (const origin of origins) {
|
|
10552
|
+
if (!origin || typeof origin !== "object") continue;
|
|
10553
|
+
const o = origin;
|
|
10554
|
+
if (pickFnUrlLogicalIdFromOriginDomainName(o["DomainName"]) !== fnUrlLogicalId) continue;
|
|
10555
|
+
const oacRef = o["OriginAccessControlId"];
|
|
10556
|
+
if (oacRef === void 0 || oacRef === "") continue;
|
|
10557
|
+
const oacLogicalId = pickOacRefLogicalId(oacRef);
|
|
10558
|
+
if (!oacLogicalId) return true;
|
|
10559
|
+
const oac = resources[oacLogicalId];
|
|
10560
|
+
if (!oac || oac.Type !== "AWS::CloudFront::OriginAccessControl") return true;
|
|
10561
|
+
const oacConfig = (oac.Properties ?? {})["OriginAccessControlConfig"];
|
|
10562
|
+
if ((oacConfig && typeof oacConfig === "object" ? oacConfig["SigningBehavior"] : void 0) === "never") continue;
|
|
10563
|
+
return true;
|
|
10564
|
+
}
|
|
10565
|
+
}
|
|
10566
|
+
return false;
|
|
10567
|
+
}
|
|
10568
|
+
/**
|
|
10569
|
+
* Unwrap an origin's `OriginAccessControlId` to the referenced
|
|
10570
|
+
* `AWS::CloudFront::OriginAccessControl` logical id. CDK synthesizes this
|
|
10571
|
+
* as `{ "Fn::GetAtt": [<id>, "Id"] }`; `{ Ref: <id> }` is also accepted.
|
|
10572
|
+
* Returns undefined for a literal id string (imported OAC) or any other
|
|
10573
|
+
* shape.
|
|
10574
|
+
*/
|
|
10575
|
+
function pickOacRefLogicalId(value) {
|
|
10576
|
+
if (!value || typeof value !== "object") return void 0;
|
|
10577
|
+
const obj = value;
|
|
10578
|
+
const ref = obj["Ref"];
|
|
10579
|
+
if (typeof ref === "string" && ref.length > 0) return ref;
|
|
10580
|
+
const getAtt = obj["Fn::GetAtt"];
|
|
10581
|
+
if (Array.isArray(getAtt) && getAtt.length === 2 && typeof getAtt[0] === "string") return getAtt[0];
|
|
10582
|
+
}
|
|
10583
|
+
/**
|
|
10518
10584
|
* Detect the canonical CDK 2.x `DomainName` shape that points a
|
|
10519
10585
|
* CloudFront Origin at a Function URL:
|
|
10520
10586
|
* {Fn::Select: [2, {Fn::Split: ['/', {Fn::GetAtt: [<id>, 'FunctionUrl']}]}]}
|
|
@@ -11151,7 +11217,8 @@ function detectFunctionUrlAuthorizer(urlResource, urlLogicalId, stack) {
|
|
|
11151
11217
|
return {
|
|
11152
11218
|
kind: "iam",
|
|
11153
11219
|
logicalId: "AWS_IAM",
|
|
11154
|
-
declaredAt: `${stack.stackName}/${urlLogicalId}
|
|
11220
|
+
declaredAt: `${stack.stackName}/${urlLogicalId}`,
|
|
11221
|
+
...isFunctionUrlOacFronted(stack.template, urlLogicalId) && { oacFronted: true }
|
|
11155
11222
|
};
|
|
11156
11223
|
}
|
|
11157
11224
|
function detectRestV1Authorizer(methodResource, methodLogicalId, stack) {
|
|
@@ -11919,11 +11986,16 @@ function defaultCredentialsLoader() {
|
|
|
11919
11986
|
* - **Signature mismatch** under the dev's own credentials → `{allow: false}`.
|
|
11920
11987
|
* The http-server maps this to 403 (REST v1 `policy-deny`).
|
|
11921
11988
|
* - **Different `Credential` access-key-id than the dev has** →
|
|
11922
|
-
* `{allow:
|
|
11923
|
-
*
|
|
11989
|
+
* `{allow: false}` by default (we can't reproduce a signing key we
|
|
11990
|
+
* don't have). With `allowUnverified` (the `--allow-unverified-sigv4`
|
|
11991
|
+
* flag, or `oacFronted` routes) → `{allow: true}` plus a one-line warn
|
|
11992
|
+
* (warn-and-pass).
|
|
11924
11993
|
* - **Valid signature with the dev's credentials** → `{allow: true}`.
|
|
11925
11994
|
* The principal id surfaced to the handler is the parsed
|
|
11926
11995
|
* `Credential` access-key-id.
|
|
11996
|
+
* - **`oacFronted` route** → the caller forces `allowUnverified` on, so
|
|
11997
|
+
* foreign / no-creds requests pass through (CloudFront re-signs origin
|
|
11998
|
+
* requests in production) and the warn lines reference CloudFront OAC.
|
|
11927
11999
|
*/
|
|
11928
12000
|
async function verifySigV4(req, loadCredentials, opts = {}) {
|
|
11929
12001
|
const logger = getLogger();
|
|
@@ -11990,7 +12062,7 @@ async function verifySigV4(req, loadCredentials, opts = {}) {
|
|
|
11990
12062
|
identityHash: void 0
|
|
11991
12063
|
};
|
|
11992
12064
|
}
|
|
11993
|
-
logger.warn(`AWS_IAM authorizer: failed to resolve local AWS credentials (${reason}). --allow-unverified-sigv4 is set; passing through with unverified principalId 'unverified-no-creds'. Do NOT trust event.requestContext.identity.accessKey in handler code.`);
|
|
12065
|
+
logger.warn(opts.oacFronted ? `AWS_IAM authorizer: Function URL is fronted by CloudFront OAC (CloudFront re-signs origin requests in production), and local AWS credentials could not be resolved (${reason}). Passing through with unverified principalId 'unverified-no-creds'. Do NOT trust event.requestContext.identity.accessKey in handler code.` : `AWS_IAM authorizer: failed to resolve local AWS credentials (${reason}). --allow-unverified-sigv4 is set; passing through with unverified principalId 'unverified-no-creds'. Do NOT trust event.requestContext.identity.accessKey in handler code.`);
|
|
11994
12066
|
return {
|
|
11995
12067
|
allow: true,
|
|
11996
12068
|
principalId: "unverified-no-creds",
|
|
@@ -12011,7 +12083,7 @@ async function verifySigV4(req, loadCredentials, opts = {}) {
|
|
|
12011
12083
|
};
|
|
12012
12084
|
}
|
|
12013
12085
|
if (!warned || !warned.has(dedupKey)) {
|
|
12014
|
-
logger.warn(`AWS_IAM authorizer: request signed with foreign access-key-id '${parsed.credentialAccessKeyId}'. --allow-unverified-sigv4 is set; passing through with unverified principalId 'unverified-foreign-identity'. Do NOT trust event.requestContext.authorizer.principalId in handler code.`);
|
|
12086
|
+
logger.warn(opts.oacFronted ? `AWS_IAM authorizer: Function URL is fronted by CloudFront OAC — in production CloudFront re-signs the origin request, so the local client's signature (access-key-id '${parsed.credentialAccessKeyId}') cannot be verified. Passing through with unverified principalId 'unverified-foreign-identity'. Do NOT trust event.requestContext.authorizer.principalId in handler code.` : `AWS_IAM authorizer: request signed with foreign access-key-id '${parsed.credentialAccessKeyId}'. --allow-unverified-sigv4 is set; passing through with unverified principalId 'unverified-foreign-identity'. Do NOT trust event.requestContext.authorizer.principalId in handler code.`);
|
|
12015
12087
|
warned?.add(dedupKey);
|
|
12016
12088
|
}
|
|
12017
12089
|
return {
|
|
@@ -12974,6 +13046,7 @@ async function runAuthorizerPass(authorizer, snapshot, matchCtx, state, opts, re
|
|
|
12974
13046
|
denyKind: "policy-deny"
|
|
12975
13047
|
};
|
|
12976
13048
|
}
|
|
13049
|
+
const oacFronted = authorizer.oacFronted === true;
|
|
12977
13050
|
const sigResult = await verifySigV4({
|
|
12978
13051
|
method: snapshot.method,
|
|
12979
13052
|
rawUrl: snapshot.rawUrl,
|
|
@@ -12981,7 +13054,8 @@ async function runAuthorizerPass(authorizer, snapshot, matchCtx, state, opts, re
|
|
|
12981
13054
|
body: snapshot.body
|
|
12982
13055
|
}, opts.sigV4CredentialsLoader, {
|
|
12983
13056
|
...opts.sigV4WarnedForeignIds && { warnedForeignIds: opts.sigV4WarnedForeignIds },
|
|
12984
|
-
|
|
13057
|
+
allowUnverified: oacFronted || opts.sigV4AllowUnverified === true,
|
|
13058
|
+
...oacFronted && { oacFronted: true }
|
|
12985
13059
|
});
|
|
12986
13060
|
if (!sigResult.allow) return {
|
|
12987
13061
|
result: { allow: false },
|
|
@@ -13936,6 +14010,7 @@ async function localStartApiCommand(target, options, extraStateProviders) {
|
|
|
13936
14010
|
const jwksWarnedUrls = /* @__PURE__ */ new Set();
|
|
13937
14011
|
let sigV4CredentialsLoader;
|
|
13938
14012
|
const sigV4WarnedForeignIds = /* @__PURE__ */ new Set();
|
|
14013
|
+
const fromCfnTipEmitted = { value: false };
|
|
13939
14014
|
/**
|
|
13940
14015
|
* One synth + discover + build pass. Returns the next-state
|
|
13941
14016
|
* material. Reused on initial boot AND every hot-reload firing.
|
|
@@ -13964,7 +14039,9 @@ async function localStartApiCommand(target, options, extraStateProviders) {
|
|
|
13964
14039
|
const targetStacks = pickTargetStacks(stacks, options.stack, cfnStackFallback, targetStackPrefix);
|
|
13965
14040
|
if (targetStacks.length === 0) throw new Error("No stacks matched. Pass --stack <name> (or --from-cfn-stack <name>) or run from a single-stack app.");
|
|
13966
14041
|
const routedStackNames = targetStacks.map((s) => s.stackName);
|
|
13967
|
-
|
|
14042
|
+
tryEmitFromCfnRedundancyTipOnce(options.fromCfnStack, routedStackNames, fromCfnTipEmitted, (routedStackName) => {
|
|
14043
|
+
logger.info(`tip: --from-cfn-stack value matches the routed stack name (${routedStackName}); you can omit the value: \`cdkl start-api ... --from-cfn-stack\` (bare flag) resolves to the same value.`);
|
|
14044
|
+
});
|
|
13968
14045
|
const routes = discoverRoutes(targetStacks);
|
|
13969
14046
|
const wsDiscovery = discoverWebSocketApis(targetStacks);
|
|
13970
14047
|
if (wsDiscovery.errors.length > 0) for (const e of wsDiscovery.errors) logger.warn(`WebSocket discovery: ${e}`);
|
|
@@ -14367,6 +14444,33 @@ function shouldEmitFromCfnRedundancyTip(fromCfnStack, routedStackNames) {
|
|
|
14367
14444
|
return fromCfnStack === routedStackNames[0];
|
|
14368
14445
|
}
|
|
14369
14446
|
/**
|
|
14447
|
+
* One-shot wrapper around `shouldEmitFromCfnRedundancyTip` for the
|
|
14448
|
+
* `--watch` hot-reload path. `synthesizeAndBuild` re-runs on every
|
|
14449
|
+
* reload firing, so without a gate the tip would re-emit on every
|
|
14450
|
+
* reload — noisy. This helper consults the caller-supplied ref:
|
|
14451
|
+
* - If the predicate fires AND the ref is still `false`, calls `emit`
|
|
14452
|
+
* and flips the ref to `true`.
|
|
14453
|
+
* - On subsequent invocations the ref is `true` and the helper is a
|
|
14454
|
+
* no-op for the rest of the ref's lifetime.
|
|
14455
|
+
* - When the predicate does NOT fire (no `--from-cfn-stack` value /
|
|
14456
|
+
* intentionally-different value / multi-stack run), the ref stays
|
|
14457
|
+
* `false` so a future reload whose synthesized stacks change in a
|
|
14458
|
+
* way that DOES make the value redundant still emits the tip once.
|
|
14459
|
+
*
|
|
14460
|
+
* The ref is owned by `localStartApiCommand` (one per server boot), so
|
|
14461
|
+
* independent server invocations get independent flags.
|
|
14462
|
+
*
|
|
14463
|
+
* @internal exported for unit tests.
|
|
14464
|
+
*/
|
|
14465
|
+
function tryEmitFromCfnRedundancyTipOnce(fromCfnStack, routedStackNames, emittedRef, emit) {
|
|
14466
|
+
if (emittedRef.value) return;
|
|
14467
|
+
if (!shouldEmitFromCfnRedundancyTip(fromCfnStack, routedStackNames)) return;
|
|
14468
|
+
const routedStackName = routedStackNames[0];
|
|
14469
|
+
if (routedStackName === void 0) return;
|
|
14470
|
+
emit(routedStackName);
|
|
14471
|
+
emittedRef.value = true;
|
|
14472
|
+
}
|
|
14473
|
+
/**
|
|
14370
14474
|
* Distinct, stable list of Lambda logical IDs reachable through any
|
|
14371
14475
|
* discovered route OR referenced by a Lambda authorizer attached to one
|
|
14372
14476
|
* of those routes. Stable order = first-occurrence order in the routes
|
|
@@ -14467,10 +14571,21 @@ function warnVpcConfigLambdas(routesWithAuth, stacks) {
|
|
|
14467
14571
|
function warnIamRoutes(routesWithAuth) {
|
|
14468
14572
|
const logger = getLogger();
|
|
14469
14573
|
const iamRoutes = [];
|
|
14470
|
-
|
|
14471
|
-
|
|
14472
|
-
|
|
14473
|
-
|
|
14574
|
+
const oacRoutes = [];
|
|
14575
|
+
for (const entry of routesWithAuth) {
|
|
14576
|
+
if (entry.authorizer?.kind !== "iam") continue;
|
|
14577
|
+
if (entry.authorizer.oacFronted === true) oacRoutes.push(entry.route.declaredAt);
|
|
14578
|
+
else iamRoutes.push(entry.route.declaredAt);
|
|
14579
|
+
}
|
|
14580
|
+
if (iamRoutes.length === 0 && oacRoutes.length === 0) return false;
|
|
14581
|
+
if (iamRoutes.length > 0) {
|
|
14582
|
+
logger.warn(`${iamRoutes.length} route(s) declare AuthorizationType: AWS_IAM — cdkl start-api verifies SigV4 signatures against your local AWS credentials, but does NOT emulate IAM policy evaluation (resource / action / condition rules). Signature-verified callers reach the handler under their own identity; downstream authorization is the dev's responsibility.`);
|
|
14583
|
+
for (const declaredAt of iamRoutes) logger.warn(` - ${declaredAt}`);
|
|
14584
|
+
}
|
|
14585
|
+
if (oacRoutes.length > 0) {
|
|
14586
|
+
logger.warn(`${oacRoutes.length} Function URL route(s) with AuthType: AWS_IAM are fronted by a CloudFront Origin Access Control. In production CloudFront re-signs the origin request, so no local client signature can be verified — cdkl start-api passes these through (warn-and-pass) WITHOUT requiring --allow-unverified-sigv4. Do NOT trust the request identity in handler code.`);
|
|
14587
|
+
for (const declaredAt of oacRoutes) logger.warn(` - ${declaredAt}`);
|
|
14588
|
+
}
|
|
14474
14589
|
return true;
|
|
14475
14590
|
}
|
|
14476
14591
|
/**
|
|
@@ -17858,4 +17973,4 @@ function createLocalStartServiceCommand(opts = {}) {
|
|
|
17858
17973
|
|
|
17859
17974
|
//#endregion
|
|
17860
17975
|
export { LocalStateSourceError as a, rejectExplicitCfnStackWithMultipleStacks as c, CfnLocalStateProvider as d, createLocalInvokeCommand as i, resolveCfnRegion as l, createLocalRunTaskCommand as n, createLocalStateProvider as o, createLocalStartApiCommand as r, isCfnFlagPresent as s, createLocalStartServiceCommand as t, resolveCfnStackName as u };
|
|
17861
|
-
//# sourceMappingURL=local-start-service-
|
|
17976
|
+
//# sourceMappingURL=local-start-service-zoDos4zT.js.map
|