@kensio/yulin 1.21.10 → 1.21.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/serve/payload-2/sim-payload-2-endpoint.d.ts +9 -0
- package/dist/serve/payload-2/sim-payload-2-request-context.js +1 -1
- package/dist/service/apigatewayv2/api/sim-http-api-lambda-proxy.factory.d.ts +6 -0
- package/dist/service/apigatewayv2/api/sim-http-api-lambda-proxy.factory.js +2 -0
- package/dist/service/apigatewayv2/api/stage/access-log/sim-http-api-access-log-format.d.ts +8 -0
- package/dist/service/apigatewayv2/api/stage/access-log/sim-http-api-access-log-format.js +36 -0
- package/dist/service/apigatewayv2/api/stage/access-log/sim-http-api-access-log-request.d.ts +53 -0
- package/dist/service/apigatewayv2/api/stage/access-log/sim-http-api-access-log-request.js +1 -0
- package/dist/service/apigatewayv2/api/stage/access-log/sim-http-api-access-log-settings.d.ts +26 -0
- package/dist/service/apigatewayv2/api/stage/access-log/sim-http-api-access-log-settings.js +44 -0
- package/dist/service/apigatewayv2/api/stage/access-log/sim-http-api-access-log-settings.type.d.ts +21 -0
- package/dist/service/apigatewayv2/api/stage/access-log/sim-http-api-access-log-settings.type.js +1 -0
- package/dist/service/apigatewayv2/api/stage/access-log/sim-http-api-access-log-stream-name.d.ts +13 -0
- package/dist/service/apigatewayv2/api/stage/access-log/sim-http-api-access-log-stream-name.js +23 -0
- package/dist/service/apigatewayv2/api/stage/access-log/sim-http-api-access-log-variables.d.ts +17 -0
- package/dist/service/apigatewayv2/api/stage/access-log/sim-http-api-access-log-variables.js +86 -0
- package/dist/service/apigatewayv2/api/stage/sim-http-api-stage.d.ts +9 -0
- package/dist/service/apigatewayv2/api/stage/sim-http-api-stage.js +9 -0
- package/dist/service/apigatewayv2/cfn/stage/sim-cfn-http-api-access-log-settings-properties.d.ts +24 -0
- package/dist/service/apigatewayv2/cfn/stage/sim-cfn-http-api-access-log-settings-properties.js +30 -0
- package/dist/service/apigatewayv2/cfn/stage/sim-cfn-http-api-stage-properties.d.ts +1 -0
- package/dist/service/apigatewayv2/cfn/stage/sim-cfn-http-api-stage-properties.js +10 -4
- package/dist/service/apigatewayv2/command/stage/sim-http-api-access-log-settings-input.d.ts +15 -0
- package/dist/service/apigatewayv2/command/stage/sim-http-api-access-log-settings-input.js +33 -0
- package/dist/service/apigatewayv2/command/stage/sim-http-api-stage-commands.js +4 -0
- package/dist/service/apigatewayv2/command/stage/stage.command.d.ts +2 -0
- package/dist/service/apigatewayv2/serve/auth/sim-http-api-route-authorize-input.d.ts +6 -0
- package/dist/service/apigatewayv2/serve/sim-api-gateway-v2-controller.d.ts +13 -15
- package/dist/service/apigatewayv2/serve/sim-api-gateway-v2-controller.js +26 -60
- package/dist/service/apigatewayv2/serve/sim-http-api-access-log-record.d.ts +31 -0
- package/dist/service/apigatewayv2/serve/sim-http-api-access-log-record.js +88 -0
- package/dist/service/apigatewayv2/serve/sim-http-api-access-log.d.ts +43 -0
- package/dist/service/apigatewayv2/serve/sim-http-api-access-log.js +72 -0
- package/dist/service/apigatewayv2/serve/sim-http-api-endpoint.js +1 -0
- package/dist/service/apigatewayv2/serve/sim-http-api-integration-input.d.ts +22 -0
- package/dist/service/apigatewayv2/serve/sim-http-api-integration-input.js +1 -0
- package/dist/service/apigatewayv2/serve/sim-http-api-integration-invocation.d.ts +5 -22
- package/dist/service/apigatewayv2/serve/sim-http-api-integration-invocation.js +17 -20
- package/dist/service/apigatewayv2/serve/sim-http-api-integration-outcome.d.ts +27 -0
- package/dist/service/apigatewayv2/serve/sim-http-api-integration-outcome.js +13 -0
- package/dist/service/apigatewayv2/serve/sim-http-api-may-invoke.d.ts +19 -0
- package/dist/service/apigatewayv2/serve/sim-http-api-may-invoke.js +19 -0
- package/dist/service/apigatewayv2/serve/sim-http-api-serve-pipeline.d.ts +27 -0
- package/dist/service/apigatewayv2/serve/sim-http-api-serve-pipeline.js +75 -0
- package/dist/service/apigatewayv2/serve/sim-http-api-served.d.ts +22 -0
- package/dist/service/apigatewayv2/serve/sim-http-api-served.js +1 -0
- package/dist/service/apigatewayv2/serve/sim-http-api-serving.d.ts +12 -0
- package/dist/service/apigatewayv2/serve/sim-http-api-serving.js +1 -0
- package/dist/service/cloudfront/controller/web-acl/sim-cf-web-acl-guard.js +1 -0
- package/dist/service/logs/group/sim-logs-arn.d.ts +15 -0
- package/dist/service/logs/group/sim-logs-arn.js +33 -0
- package/dist/service/wafv2/association/sim-waf-associations.js +1 -1
- package/dist/service/wafv2/cfn/web-acl/sim-cfn-waf-web-acl-config.js +1 -0
- package/dist/service/wafv2/command/web-acl/sim-waf-web-acl-output.js +1 -0
- package/dist/service/wafv2/command/web-acl/sim-wafv2-unsimulated-web-acl-input.js +0 -6
- package/dist/service/wafv2/command/web-acl/sim-wafv2-web-acl-commands.js +1 -0
- package/dist/service/wafv2/command/web-acl/web-acl.command.d.ts +3 -1
- package/dist/service/wafv2/evaluate/sim-waf-evaluation-request.d.ts +8 -0
- package/dist/service/wafv2/evaluate/sim-waf-inspected-request.d.ts +14 -1
- package/dist/service/wafv2/evaluate/sim-waf-inspected-request.js +7 -1
- package/dist/service/wafv2/index.d.ts +2 -1
- package/dist/service/wafv2/index.js +2 -1
- package/dist/service/wafv2/managed/sim-waf-managed-request.js +1 -2
- package/dist/service/wafv2/sim-wafv2.js +2 -1
- package/dist/service/wafv2/statement/sim-waf-field-content.d.ts +9 -6
- package/dist/service/wafv2/statement/sim-waf-field-content.js +16 -10
- package/dist/service/wafv2/statement/sim-waf-set-field.js +3 -3
- package/dist/service/wafv2/web-acl/sim-waf-association-config.d.ts +54 -0
- package/dist/service/wafv2/web-acl/sim-waf-association-config.js +92 -0
- package/dist/service/wafv2/web-acl/sim-waf-web-acl-configuration.d.ts +2 -0
- package/dist/service/wafv2/web-acl/sim-waf-web-acl.d.ts +9 -0
- package/dist/service/wafv2/web-acl/sim-waf-web-acl.js +15 -0
- package/docs/services/apigatewayv2/README.md +162 -6
- package/docs/services/wafv2/README.md +19 -5
- package/package.json +1 -1
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { SimWafInvalidParameterException, SimWafUnsimulatedInputException, } from "../error/sim-wafv2.error.js";
|
|
2
|
+
/**
|
|
3
|
+
* How much of a request body WAF reads for a resource type given no
|
|
4
|
+
* `AssociationConfig`.
|
|
5
|
+
*
|
|
6
|
+
* CloudFront, API Gateway and Cognito all default to 16 KB. A load balancer
|
|
7
|
+
* and an AppSync API are fixed at 8 KB, and a web ACL cannot be put in front
|
|
8
|
+
* of either one here.
|
|
9
|
+
*/
|
|
10
|
+
export const simWafBodyInspectionLimitBytes = 16_384;
|
|
11
|
+
/**
|
|
12
|
+
* The four sizes `DefaultSizeInspectionLimit` names, in bytes.
|
|
13
|
+
*/
|
|
14
|
+
const inspectionLimits = new Map([
|
|
15
|
+
["KB_16", 16_384],
|
|
16
|
+
["KB_32", 32_768],
|
|
17
|
+
["KB_48", 49_152],
|
|
18
|
+
["KB_64", 65_536],
|
|
19
|
+
]);
|
|
20
|
+
const simulatedResourceTypes = [
|
|
21
|
+
"CLOUDFRONT",
|
|
22
|
+
"API_GATEWAY",
|
|
23
|
+
"COGNITO_USER_POOL",
|
|
24
|
+
];
|
|
25
|
+
/**
|
|
26
|
+
* How much of a request body a web ACL's rules read, by resource type.
|
|
27
|
+
*
|
|
28
|
+
* A web ACL holds one of these whether or not it was written with an
|
|
29
|
+
* `AssociationConfig`, and a resource type the config left out reads the
|
|
30
|
+
* default 16 KB.
|
|
31
|
+
*/
|
|
32
|
+
export class SimWafBodyInspectionLimits {
|
|
33
|
+
limits;
|
|
34
|
+
constructor(limits) {
|
|
35
|
+
this.limits = limits;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Read the limits an `AssociationConfig` sets, refusing anything WAF would
|
|
39
|
+
* refuse and anything this simulation cannot apply.
|
|
40
|
+
*/
|
|
41
|
+
static read(input) {
|
|
42
|
+
const configured = Object.entries(input?.RequestBody ?? {});
|
|
43
|
+
const limits = new Map();
|
|
44
|
+
for (const [resourceType, config] of configured) {
|
|
45
|
+
limits.set(simulatedResourceType(resourceType), inspectionLimit(resourceType, config));
|
|
46
|
+
}
|
|
47
|
+
return new SimWafBodyInspectionLimits(limits);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* How many bytes of a body a request reaching one resource type is inspected
|
|
51
|
+
* for.
|
|
52
|
+
*
|
|
53
|
+
* A request evaluated against a web ACL that no resource holds names no
|
|
54
|
+
* resource type, and reads the default.
|
|
55
|
+
*/
|
|
56
|
+
bytesFor(resourceType) {
|
|
57
|
+
if (resourceType === undefined) {
|
|
58
|
+
return simWafBodyInspectionLimitBytes;
|
|
59
|
+
}
|
|
60
|
+
return this.limits.get(resourceType) ?? simWafBodyInspectionLimitBytes;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Hold a `RequestBody` key to the resource types this simulation associates.
|
|
65
|
+
*/
|
|
66
|
+
function simulatedResourceType(resourceType) {
|
|
67
|
+
const simulated = simulatedResourceTypes.find((candidate) => candidate === resourceType);
|
|
68
|
+
if (simulated === undefined) {
|
|
69
|
+
throw new SimWafUnsimulatedInputException(`AssociationConfig sets the body inspection limit for ${resourceType} ` +
|
|
70
|
+
`resources, and ${simulatedResourceTypes.join(" and ")} are the ` +
|
|
71
|
+
`types a web ACL goes in front of in Yulin`);
|
|
72
|
+
}
|
|
73
|
+
return simulated;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Read one resource type's `DefaultSizeInspectionLimit`.
|
|
77
|
+
*
|
|
78
|
+
* An entry carrying no size is refused the same way one carrying a size WAF
|
|
79
|
+
* has no setting for is. A template writes this part of a web ACL by hand, and
|
|
80
|
+
* an entry that says nothing about the limit it is there to set is a mistake
|
|
81
|
+
* worth reporting as one.
|
|
82
|
+
*/
|
|
83
|
+
function inspectionLimit(resourceType, config) {
|
|
84
|
+
const limit = config?.DefaultSizeInspectionLimit;
|
|
85
|
+
const bytes = limit === undefined ? undefined : inspectionLimits.get(limit);
|
|
86
|
+
if (bytes === undefined) {
|
|
87
|
+
throw new SimWafInvalidParameterException(`Error reason: The DefaultSizeInspectionLimit ${String(limit)} is one ` +
|
|
88
|
+
`of ${inspectionLimits.keys().toArray().join(", ")}, field: ` +
|
|
89
|
+
`ASSOCIATION_CONFIG, parameter: ${resourceType}`);
|
|
90
|
+
}
|
|
91
|
+
return bytes;
|
|
92
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SimWafAction } from "./sim-waf-action.js";
|
|
2
|
+
import type { SimWafAssociationConfigInput } from "./sim-waf-association-config.js";
|
|
2
3
|
import type { SimWafActionInput } from "./sim-waf-action.type.js";
|
|
3
4
|
import type { SimWafCustomResponseBodies } from "./sim-waf-custom-response.type.js";
|
|
4
5
|
import type { SimWafRuleInput } from "./sim-waf-rule.type.js";
|
|
@@ -11,6 +12,7 @@ export interface SimWafWebAclConfiguration {
|
|
|
11
12
|
readonly customResponseBodies?: SimWafCustomResponseBodies | undefined;
|
|
12
13
|
readonly visibilityConfig?: unknown;
|
|
13
14
|
readonly description?: string | undefined;
|
|
15
|
+
readonly associationConfig?: SimWafAssociationConfigInput | undefined;
|
|
14
16
|
}
|
|
15
17
|
/**
|
|
16
18
|
* Read the action a request no rule claims is given.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type SimWafDecision } from "../evaluate/sim-waf-decision.js";
|
|
2
2
|
import type { SimWafInspectedRequest } from "../evaluate/sim-waf-inspected-request.js";
|
|
3
3
|
import { SimWafResource, type SimWafResourceProperties } from "../resource/sim-waf-resource.js";
|
|
4
|
+
import { type SimWafBodyInspectionResourceType } from "./sim-waf-association-config.js";
|
|
4
5
|
import type { SimWafWebAclRuleScope } from "./sim-waf-rule.type.js";
|
|
5
6
|
import { type SimWafWebAclConfiguration } from "./sim-waf-web-acl-configuration.js";
|
|
6
7
|
interface SimWafWebAclProperties extends SimWafResourceProperties, SimWafWebAclRuleScope {
|
|
@@ -43,6 +44,14 @@ export declare class SimWafWebAcl extends SimWafResource {
|
|
|
43
44
|
* refuses an update keeps the rules it had rather than being left with none.
|
|
44
45
|
*/
|
|
45
46
|
reconfigure(configuration: SimWafWebAclConfiguration, lockToken: string | undefined): void;
|
|
47
|
+
/**
|
|
48
|
+
* How many bytes of a request body this web ACL reads in front of one
|
|
49
|
+
* resource type.
|
|
50
|
+
*
|
|
51
|
+
* `AssociationConfig` raises it per resource type, and a web ACL written
|
|
52
|
+
* without one reads the 16 KB default everything it protects has.
|
|
53
|
+
*/
|
|
54
|
+
bodyInspectionLimitBytes(resourceType: SimWafBodyInspectionResourceType | undefined): number;
|
|
46
55
|
/**
|
|
47
56
|
* Decide what happens to one request.
|
|
48
57
|
*/
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { simWafDecision, } from "../evaluate/sim-waf-decision.js";
|
|
2
2
|
import { simWafEvaluateRules } from "../evaluate/sim-waf-evaluate-rules.js";
|
|
3
3
|
import { SimWafResource, } from "../resource/sim-waf-resource.js";
|
|
4
|
+
import { SimWafBodyInspectionLimits, } from "./sim-waf-association-config.js";
|
|
4
5
|
import { compileSimWafWebAclRules } from "./sim-waf-rules.js";
|
|
5
6
|
import { simWafWebAclCapacity } from "./sim-waf-web-acl-capacity.js";
|
|
6
7
|
import { readSimWafDefaultAction, } from "./sim-waf-web-acl-configuration.js";
|
|
@@ -29,6 +30,7 @@ export class SimWafWebAcl extends SimWafResource {
|
|
|
29
30
|
#defaultAction;
|
|
30
31
|
#rules;
|
|
31
32
|
#capacity;
|
|
33
|
+
#bodyInspectionLimits;
|
|
32
34
|
constructor(properties) {
|
|
33
35
|
super("webacl", properties);
|
|
34
36
|
this.#scope = properties;
|
|
@@ -36,6 +38,7 @@ export class SimWafWebAcl extends SimWafResource {
|
|
|
36
38
|
this.#defaultAction = readSimWafDefaultAction(properties.configuration);
|
|
37
39
|
this.#rules = compileSimWafWebAclRules(properties.configuration, this.#scope);
|
|
38
40
|
this.#capacity = simWafWebAclCapacity(properties.configuration.rules);
|
|
41
|
+
this.#bodyInspectionLimits = SimWafBodyInspectionLimits.read(properties.configuration.associationConfig);
|
|
39
42
|
this.labelNamespace =
|
|
40
43
|
`awswaf:${properties.accountRegionScope.accountId}:webacl:` +
|
|
41
44
|
`${properties.name}:`;
|
|
@@ -61,12 +64,24 @@ export class SimWafWebAcl extends SimWafResource {
|
|
|
61
64
|
reconfigure(configuration, lockToken) {
|
|
62
65
|
const defaultAction = readSimWafDefaultAction(configuration);
|
|
63
66
|
const rules = compileSimWafWebAclRules(configuration, this.#scope);
|
|
67
|
+
const bodyInspectionLimits = SimWafBodyInspectionLimits.read(configuration.associationConfig);
|
|
64
68
|
this.takeLock(lockToken);
|
|
65
69
|
this.replaceDescription(configuration.description);
|
|
66
70
|
this.#configuration = configuration;
|
|
67
71
|
this.#defaultAction = defaultAction;
|
|
68
72
|
this.#rules = rules;
|
|
69
73
|
this.#capacity = simWafWebAclCapacity(configuration.rules);
|
|
74
|
+
this.#bodyInspectionLimits = bodyInspectionLimits;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* How many bytes of a request body this web ACL reads in front of one
|
|
78
|
+
* resource type.
|
|
79
|
+
*
|
|
80
|
+
* `AssociationConfig` raises it per resource type, and a web ACL written
|
|
81
|
+
* without one reads the 16 KB default everything it protects has.
|
|
82
|
+
*/
|
|
83
|
+
bodyInspectionLimitBytes(resourceType) {
|
|
84
|
+
return this.#bodyInspectionLimits.bytesFor(resourceType);
|
|
70
85
|
}
|
|
71
86
|
/**
|
|
72
87
|
* Decide what happens to one request.
|
|
@@ -666,8 +666,146 @@ route configured that way is served unthrottled.
|
|
|
666
666
|
`GetStages` answers with the settings a stage was created with. `AWS::ApiGatewayV2::Stage` deploys
|
|
667
667
|
both properties as well (see [CloudFormation](#cloudformation)). The three members of `RouteSettings`
|
|
668
668
|
that say nothing about throttling, `DetailedMetricsEnabled`, `LoggingLevel` and `DataTraceEnabled`,
|
|
669
|
-
are refused by `CreateStage`.
|
|
670
|
-
`
|
|
669
|
+
are refused by `CreateStage`. They are execution logging, which is a different log from the one
|
|
670
|
+
[`AccessLogSettings` writes](#writing-a-stages-access-log). A template carrying one deploys, and the
|
|
671
|
+
member it named is recorded on `stack.ignoredProperties`.
|
|
672
|
+
|
|
673
|
+
## Writing a stage's access log
|
|
674
|
+
|
|
675
|
+
`AccessLogSettings` sends one line per request to a CloudWatch Logs log group. `DestinationArn` names
|
|
676
|
+
the group and `Format` is the line, with `$context` variables substituted from the request that
|
|
677
|
+
produced it.
|
|
678
|
+
|
|
679
|
+
The line is written whether or not an integration ran. A request the stage throttle refused, one a
|
|
680
|
+
Lambda authorizer denied, and one with no identity source header all reach the log group, and for
|
|
681
|
+
those the access log is the only record of the request.
|
|
682
|
+
|
|
683
|
+
```typescript sim-apigatewayv2-access-log
|
|
684
|
+
/**
|
|
685
|
+
* Recording a simulated HTTP API stage's access log, including a request the
|
|
686
|
+
* stage's throttle refused.
|
|
687
|
+
*/
|
|
688
|
+
|
|
689
|
+
import {
|
|
690
|
+
CreateApiCommand,
|
|
691
|
+
CreateIntegrationCommand,
|
|
692
|
+
CreateRouteCommand,
|
|
693
|
+
CreateStageCommand,
|
|
694
|
+
} from "@aws-sdk/client-apigatewayv2";
|
|
695
|
+
import {
|
|
696
|
+
CreateLogGroupCommand,
|
|
697
|
+
FilterLogEventsCommand,
|
|
698
|
+
} from "@aws-sdk/client-cloudwatch-logs";
|
|
699
|
+
import {
|
|
700
|
+
AddPermissionCommand,
|
|
701
|
+
CreateFunctionCommand,
|
|
702
|
+
} from "@aws-sdk/client-lambda";
|
|
703
|
+
|
|
704
|
+
import { SimAws } from "@kensio/yulin";
|
|
705
|
+
import { makeLambdaZipFileInput } from "@kensio/yulin/lambda";
|
|
706
|
+
import { serveSimAws } from "@kensio/yulin/serve";
|
|
707
|
+
|
|
708
|
+
const simAws = new SimAws();
|
|
709
|
+
const logGroupName = "/aws/vendedlogs/user-api";
|
|
710
|
+
|
|
711
|
+
await simAws.logs().createLogGroup(new CreateLogGroupCommand({ logGroupName }));
|
|
712
|
+
|
|
713
|
+
const { FunctionArn } = await simAws.lambda().createFunction(
|
|
714
|
+
new CreateFunctionCommand({
|
|
715
|
+
FunctionName: "users",
|
|
716
|
+
Role: "arn:aws:iam::111111111111:role/UsersRole",
|
|
717
|
+
Code: {
|
|
718
|
+
ZipFile: makeLambdaZipFileInput(() => ({
|
|
719
|
+
statusCode: 200,
|
|
720
|
+
headers: { "content-type": "text/plain" },
|
|
721
|
+
body: "ok",
|
|
722
|
+
})),
|
|
723
|
+
},
|
|
724
|
+
}),
|
|
725
|
+
);
|
|
726
|
+
|
|
727
|
+
const apiGateway = simAws.apiGatewayV2();
|
|
728
|
+
const { ApiId, ApiEndpoint } = await apiGateway.createApi(
|
|
729
|
+
new CreateApiCommand({ Name: "users", ProtocolType: "HTTP" }),
|
|
730
|
+
);
|
|
731
|
+
|
|
732
|
+
const { IntegrationId } = await apiGateway.createIntegration(
|
|
733
|
+
new CreateIntegrationCommand({
|
|
734
|
+
ApiId,
|
|
735
|
+
IntegrationType: "AWS_PROXY",
|
|
736
|
+
IntegrationUri: FunctionArn,
|
|
737
|
+
PayloadFormatVersion: "2.0",
|
|
738
|
+
}),
|
|
739
|
+
);
|
|
740
|
+
|
|
741
|
+
await apiGateway.createRoute(
|
|
742
|
+
new CreateRouteCommand({
|
|
743
|
+
ApiId,
|
|
744
|
+
RouteKey: "GET /user/profile",
|
|
745
|
+
Target: `integrations/${IntegrationId}`,
|
|
746
|
+
}),
|
|
747
|
+
);
|
|
748
|
+
|
|
749
|
+
await apiGateway.createStage(
|
|
750
|
+
new CreateStageCommand({
|
|
751
|
+
ApiId,
|
|
752
|
+
StageName: "$default",
|
|
753
|
+
AutoDeploy: true,
|
|
754
|
+
DefaultRouteSettings: { ThrottlingRateLimit: 1, ThrottlingBurstLimit: 1 },
|
|
755
|
+
AccessLogSettings: {
|
|
756
|
+
DestinationArn: `arn:aws:logs:us-east-1:888888888888:log-group:${logGroupName}:*`,
|
|
757
|
+
Format:
|
|
758
|
+
"$context.httpMethod $context.path $context.status " +
|
|
759
|
+
"$context.error.message",
|
|
760
|
+
},
|
|
761
|
+
}),
|
|
762
|
+
);
|
|
763
|
+
|
|
764
|
+
await simAws.lambda().addPermission(
|
|
765
|
+
new AddPermissionCommand({
|
|
766
|
+
FunctionName: "users",
|
|
767
|
+
StatementId: "api-gateway-invoke",
|
|
768
|
+
Action: "lambda:InvokeFunction",
|
|
769
|
+
Principal: "apigateway.amazonaws.com",
|
|
770
|
+
SourceArn: `arn:aws:execute-api:us-east-1:888888888888:${ApiId}/*/*`,
|
|
771
|
+
}),
|
|
772
|
+
);
|
|
773
|
+
|
|
774
|
+
const srv = await serveSimAws({ simAws });
|
|
775
|
+
simAws.clock().freeze();
|
|
776
|
+
|
|
777
|
+
const profile = srv.localUrl(`${ApiEndpoint}/user/profile`);
|
|
778
|
+
await fetch(profile);
|
|
779
|
+
await fetch(profile);
|
|
780
|
+
|
|
781
|
+
const { events } = await simAws
|
|
782
|
+
.logs()
|
|
783
|
+
.filterLogEvents(new FilterLogEventsCommand({ logGroupName }));
|
|
784
|
+
|
|
785
|
+
const lines = events ?? [];
|
|
786
|
+
|
|
787
|
+
for (const event of lines) {
|
|
788
|
+
console.log(event.message);
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
await srv.close();
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
The served request and the throttled one are both there. A variable with no value renders as a dash,
|
|
795
|
+
which is what `$context.error.message` does for the request the integration answered:
|
|
796
|
+
|
|
797
|
+
```text
|
|
798
|
+
GET /user/profile 200 -
|
|
799
|
+
GET /user/profile 429 Too Many Requests
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
The lines go to the log group and nowhere else. An API answering a few hundred requests in one test
|
|
803
|
+
file would otherwise bury the suite's own output, so nothing here is forwarded to the console the way
|
|
804
|
+
a [Lambda handler's output](https://yulinsim.dev/services/lambda/#capturing-handler-output) is.
|
|
805
|
+
|
|
806
|
+
A `DestinationArn` naming a log group nothing has created yet is written to anyway, and the group is
|
|
807
|
+
made by the first line. `Format` is copied through apart from its `$context` references, so a JSON
|
|
808
|
+
format string arrives as JSON.
|
|
671
809
|
|
|
672
810
|
## Protecting a route with a Cognito user pool
|
|
673
811
|
|
|
@@ -2287,7 +2425,7 @@ parts behaves differently to the template. The simulated properties are:
|
|
|
2287
2425
|
- `Integration`: `ApiId`, `IntegrationType`, `IntegrationUri`, `PayloadFormatVersion`, `Description`
|
|
2288
2426
|
- `Route`: `ApiId`, `RouteKey`, `Target`, `AuthorizationType`, `AuthorizerId`, `AuthorizationScopes`
|
|
2289
2427
|
- `Stage`: `ApiId`, `StageName`, `AutoDeploy`, `StageVariables`, `Description`,
|
|
2290
|
-
`DefaultRouteSettings`, `RouteSettings`
|
|
2428
|
+
`DefaultRouteSettings`, `RouteSettings`, `AccessLogSettings`
|
|
2291
2429
|
|
|
2292
2430
|
CDK's `HttpIamAuthorizer` deploys too. It emits no `AWS::ApiGatewayV2::Authorizer` and no
|
|
2293
2431
|
`AuthorizerId`, only `AuthorizationType: "AWS_IAM"` on the `Route`, and the deployed route then
|
|
@@ -2436,6 +2574,9 @@ the simulation without being given one. See the
|
|
|
2436
2574
|
under their own path segment, including stage variables
|
|
2437
2575
|
- `DefaultRouteSettings` and `RouteSettings` throttling, with a token bucket per route refilling
|
|
2438
2576
|
against the simulated clock and a 429 for the requests past it
|
|
2577
|
+
- `AccessLogSettings`, writing one line per request to the log group `DestinationArn` names, with the
|
|
2578
|
+
`Format` string's `$context` variables substituted, including for a request the throttle or an
|
|
2579
|
+
authorizer refused before any integration ran
|
|
2439
2580
|
- Serving the generated endpoint through `serveSimAws`, invoking the integrated function with a
|
|
2440
2581
|
payload format 2.0 event and turning its result back into an HTTP response
|
|
2441
2582
|
- The invoke permission of an integration's function and of an authorizer's, each evaluated against
|
|
@@ -2474,9 +2615,24 @@ Current documented limitations:
|
|
|
2474
2615
|
- Deployments are outside the simulation, so `CreateStage` requires `AutoDeploy: true`. A stage
|
|
2475
2616
|
without it serves whichever Deployment it was given, which on real AWS is nothing until one is
|
|
2476
2617
|
created.
|
|
2477
|
-
- `
|
|
2478
|
-
|
|
2479
|
-
|
|
2618
|
+
- Any option `CreateStage` takes and this one lacks is refused. `RouteSettings` and
|
|
2619
|
+
`DefaultRouteSettings` are taken, and only their throttling members are read.
|
|
2620
|
+
`DetailedMetricsEnabled`, `LoggingLevel` and `DataTraceEnabled` are refused by name. Those three
|
|
2621
|
+
are execution logging, which is a different log from the access log and is not simulated.
|
|
2622
|
+
- An access log `DestinationArn` has to name a CloudWatch Logs log group. A Kinesis Data Firehose
|
|
2623
|
+
delivery stream, which a REST API stage may name, is refused.
|
|
2624
|
+
- The access log variables carrying a value are `accountId`, `apiId`, `domainName`, `domainPrefix`,
|
|
2625
|
+
`stage`, `routeKey`, `httpMethod`, `path`, `protocol`, `requestId`, `extendedRequestId`,
|
|
2626
|
+
`requestTime`, `requestTimeEpoch`, `status`, `responseLength`, `responseLatency`,
|
|
2627
|
+
`identity.sourceIp`, `identity.userAgent`, `integrationStatus`, `integrationLatency`,
|
|
2628
|
+
`integrationErrorMessage`, `authorizer.error`, `error.message`, `error.messageString`,
|
|
2629
|
+
`customDomain.basePathMatched`, the `integration.*` aliases, and `authorizer.claims.<property>` and
|
|
2630
|
+
`authorizer.<property>` from the route's authorizer. Anything else AWS documents renders as a dash.
|
|
2631
|
+
- `responseLatency` and `integrationLatency` are simulated milliseconds, so a test holding the clock
|
|
2632
|
+
still logs zero rather than however long the process took.
|
|
2633
|
+
- The access log stream is named for the hour it was written in, dated from the simulation's clock.
|
|
2634
|
+
Real API Gateway appends an identifier this simulation has no counterpart for, so a test should
|
|
2635
|
+
filter the log group rather than name the stream.
|
|
2480
2636
|
- `AWS_PROXY` is the only integration type, and its URI must name a Lambda function ARN, written
|
|
2481
2637
|
either as that ARN or as the
|
|
2482
2638
|
`arn:aws:apigateway:<region>:lambda:path/2015-03-31/functions/<function-arn>/invocations` form.
|
|
@@ -173,9 +173,22 @@ resource, and `CreateWebACL` here refuses it too.
|
|
|
173
173
|
Matching is case sensitive, as it is on AWS. A rule that means to ignore case says so with a
|
|
174
174
|
`LOWERCASE` transformation and a lower case search string.
|
|
175
175
|
|
|
176
|
-
WAF stops reading a
|
|
177
|
-
|
|
178
|
-
|
|
176
|
+
WAF stops reading a header set or a cookie set at 8 KB. A body runs to 16 KB, the figure
|
|
177
|
+
CloudFront, API Gateway and Cognito all send for inspection, and a web ACL raises it to 32, 48 or 64
|
|
178
|
+
KB with `AssociationConfig`:
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
AssociationConfig: {
|
|
182
|
+
RequestBody: {
|
|
183
|
+
CLOUDFRONT: { DefaultSizeInspectionLimit: "KB_64" },
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The key is the resource type the raised limit applies to, and `CLOUDFRONT`, `API_GATEWAY` and
|
|
189
|
+
`COGNITO_USER_POOL` are the three a web ACL goes in front of here. The rule's `OversizeHandling`
|
|
190
|
+
says what content past the limit counts as. `MATCH` and `NO_MATCH` settle the statement without
|
|
191
|
+
looking, and `CONTINUE` inspects as much as WAF would have read.
|
|
179
192
|
|
|
180
193
|
## Rate limiting
|
|
181
194
|
|
|
@@ -1363,8 +1376,9 @@ fields to match. The `Captcha` and `Challenge` actions are refused, along with t
|
|
|
1363
1376
|
`ChallengeConfig` and `TokenDomains` that configure them, because a browser has to answer them.
|
|
1364
1377
|
|
|
1365
1378
|
Tags, logging, sampled requests and CloudWatch metrics for a web ACL are not simulated.
|
|
1366
|
-
`
|
|
1367
|
-
|
|
1379
|
+
`DataProtectionConfig`, `OnSourceDDoSProtectionConfig` and `ApplicationConfig` are refused for the
|
|
1380
|
+
same reason, each naming what it would have configured. An `AssociationConfig` naming a resource
|
|
1381
|
+
type this simulation has no association for is refused too.
|
|
1368
1382
|
|
|
1369
1383
|
## Supported operations
|
|
1370
1384
|
|
package/package.json
CHANGED