@kensio/yulin 1.21.11 → 1.21.13
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/service/cloudfront/controller/web-acl/sim-cf-web-acl-guard.js +1 -0
- package/dist/service/dynamodb/command/authorize/sim-dynamodb-authorizer.d.ts +1 -1
- package/dist/service/dynamodb/command/authorize/sim-dynamodb-authorizer.js +23 -4
- package/dist/service/dynamodb/command/authorize/sim-dynamodb-leading-keys.d.ts +33 -0
- package/dist/service/dynamodb/command/authorize/sim-dynamodb-leading-keys.js +45 -0
- package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-get-item.js +2 -0
- package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-tables.d.ts +10 -2
- package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-tables.js +1 -1
- package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-write-item.js +7 -1
- package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-write.d.ts +6 -0
- package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-write.js +8 -8
- package/dist/service/dynamodb/command/item/sim-dynamodb-delete-item.js +2 -1
- package/dist/service/dynamodb/command/item/sim-dynamodb-get-item.js +2 -1
- package/dist/service/dynamodb/command/item/sim-dynamodb-put-item.js +2 -1
- package/dist/service/dynamodb/command/item/sim-dynamodb-update-item.js +2 -1
- package/dist/service/dynamodb/command/query/sim-dynamodb-query.js +6 -1
- package/dist/service/dynamodb/command/table/sim-dynamodb-table-access.d.ts +16 -6
- package/dist/service/dynamodb/command/table/sim-dynamodb-table-access.js +34 -8
- package/dist/service/iam/authorize/match/condition/sim-iam-condition-operator.d.ts +4 -1
- package/dist/service/iam/authorize/match/condition/string/all-values/sim-iam-for-all-values-string-condition-operator.d.ts +5 -1
- package/dist/service/iam/authorize/match/condition/string/all-values/sim-iam-for-all-values-string-condition-operator.js +5 -1
- 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/dynamodb/README.md +108 -0
- package/docs/services/iam/README.md +8 -2
- package/docs/services/wafv2/README.md +19 -5
- package/package.json +2 -2
|
@@ -22,6 +22,15 @@ export interface SimWafInspectedRequest {
|
|
|
22
22
|
readonly host: string;
|
|
23
23
|
/** The request body, or nothing when the request carried none. */
|
|
24
24
|
readonly body: Uint8Array | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* How many bytes of the body a rule inspecting it reads.
|
|
27
|
+
*
|
|
28
|
+
* This travels with the request because it belongs to the resource the
|
|
29
|
+
* request reached rather than to the rule. A rule is compiled once, when the
|
|
30
|
+
* web ACL is written, and the same compiled rule then runs in front of a
|
|
31
|
+
* distribution and a REST API stage with a different limit for each.
|
|
32
|
+
*/
|
|
33
|
+
readonly bodyInspectionLimitBytes: number;
|
|
25
34
|
/** The labels the rules that have run so far added to this request. */
|
|
26
35
|
readonly labels: SimWafRequestLabels;
|
|
27
36
|
}
|
|
@@ -36,5 +45,9 @@ export interface SimWafInspectedRequest {
|
|
|
36
45
|
* simulated endpoint is served under `*.sim-aws.localhost`, and a rule reading
|
|
37
46
|
* the raw Host header would see that suffix on every request that reached
|
|
38
47
|
* anything at all.
|
|
48
|
+
*
|
|
49
|
+
* The body inspection limit defaults to the 16 KB every resource type this
|
|
50
|
+
* simulation protects reads by default. A web ACL raising it with
|
|
51
|
+
* `AssociationConfig` passes the raised figure in.
|
|
39
52
|
*/
|
|
40
|
-
export declare function simWafInspectedRequest(request: Request, body?: Uint8Array): SimWafInspectedRequest;
|
|
53
|
+
export declare function simWafInspectedRequest(request: Request, body?: Uint8Array, bodyInspectionLimitBytes?: number): SimWafInspectedRequest;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { simAwsRequestHostname } from "../../../serve/http/url/sim-aws-request-hostname.js";
|
|
2
|
+
import { simWafBodyInspectionLimitBytes } from "../web-acl/sim-waf-association-config.js";
|
|
2
3
|
import { SimWafRequestLabels } from "./sim-waf-request-labels.js";
|
|
3
4
|
/**
|
|
4
5
|
* Read the parts of an HTTP request that a web ACL inspects.
|
|
@@ -11,8 +12,12 @@ import { SimWafRequestLabels } from "./sim-waf-request-labels.js";
|
|
|
11
12
|
* simulated endpoint is served under `*.sim-aws.localhost`, and a rule reading
|
|
12
13
|
* the raw Host header would see that suffix on every request that reached
|
|
13
14
|
* anything at all.
|
|
15
|
+
*
|
|
16
|
+
* The body inspection limit defaults to the 16 KB every resource type this
|
|
17
|
+
* simulation protects reads by default. A web ACL raising it with
|
|
18
|
+
* `AssociationConfig` passes the raised figure in.
|
|
14
19
|
*/
|
|
15
|
-
export function simWafInspectedRequest(request, body) {
|
|
20
|
+
export function simWafInspectedRequest(request, body, bodyInspectionLimitBytes = simWafBodyInspectionLimitBytes) {
|
|
16
21
|
const url = new URL(request.url);
|
|
17
22
|
return {
|
|
18
23
|
method: request.method,
|
|
@@ -24,6 +29,7 @@ export function simWafInspectedRequest(request, body) {
|
|
|
24
29
|
headers: request.headers,
|
|
25
30
|
host: simAwsRequestHostname(request),
|
|
26
31
|
body,
|
|
32
|
+
bodyInspectionLimitBytes,
|
|
27
33
|
labels: new SimWafRequestLabels(),
|
|
28
34
|
};
|
|
29
35
|
}
|
|
@@ -29,7 +29,8 @@ export type { SimWafManagedRuleTier } from "./managed/sim-waf-managed-rule.type.
|
|
|
29
29
|
export type { SimWafManagedRuleGroupStatementInput, SimWafOverrideActionInput, SimWafRuleActionOverrideInput, } from "./managed/sim-waf-managed-group.type.js";
|
|
30
30
|
export { simWafBlockedHttpResponse, type SimWafBlockedResponse, simWafDefaultBlockedResponse, } from "./evaluate/sim-waf-blocked-response.js";
|
|
31
31
|
export { type SimWafInspectedRequest, simWafInspectedRequest, } from "./evaluate/sim-waf-inspected-request.js";
|
|
32
|
-
export {
|
|
32
|
+
export { simWafHeaderInspectionLimitBytes } from "./statement/sim-waf-field-content.js";
|
|
33
|
+
export { type SimWafAssociationConfigInput, type SimWafBodyInspectionResourceType, simWafBodyInspectionLimitBytes, } from "./web-acl/sim-waf-association-config.js";
|
|
33
34
|
export type { SimWafRateBasedStatementInput } from "./statement/sim-waf-rate-based.type.js";
|
|
34
35
|
export type { SimWafStatementInput } from "./statement/sim-waf-statement.type.js";
|
|
35
36
|
export { SimWafAssociatedItemException, SimWafDeclarationError, SimWafDuplicateItemException, SimWafError, type SimWafErrorMetadata, SimWafInvalidParameterException, SimWafNonexistentItemException, SimWafOptimisticLockException, SimWafUnavailableEntityException, SimWafUnsimulatedInputException, } from "./error/sim-wafv2.error.js";
|
|
@@ -19,5 +19,6 @@ export { SimWafRequestLabels } from "./evaluate/sim-waf-request-labels.js";
|
|
|
19
19
|
export { SimWafManagedRules, } from "./managed/sim-waf-managed-rules.js";
|
|
20
20
|
export { simWafBlockedHttpResponse, simWafDefaultBlockedResponse, } from "./evaluate/sim-waf-blocked-response.js";
|
|
21
21
|
export { simWafInspectedRequest, } from "./evaluate/sim-waf-inspected-request.js";
|
|
22
|
-
export {
|
|
22
|
+
export { simWafHeaderInspectionLimitBytes } from "./statement/sim-waf-field-content.js";
|
|
23
|
+
export { simWafBodyInspectionLimitBytes, } from "./web-acl/sim-waf-association-config.js";
|
|
23
24
|
export { SimWafAssociatedItemException, SimWafDeclarationError, SimWafDuplicateItemException, SimWafError, SimWafInvalidParameterException, SimWafNonexistentItemException, SimWafOptimisticLockException, SimWafUnavailableEntityException, SimWafUnsimulatedInputException, } from "./error/sim-wafv2.error.js";
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { simWafInspectionLimitBytes } from "../statement/sim-waf-field-content.js";
|
|
2
1
|
import { simWafQueryArguments } from "../statement/sim-waf-request-fields.js";
|
|
3
2
|
const encoder = new TextEncoder();
|
|
4
3
|
const decoder = new TextDecoder();
|
|
@@ -22,7 +21,7 @@ export function simWafManagedRequestParts(request) {
|
|
|
22
21
|
headerValues: [...request.headers].map(([, value]) => value),
|
|
23
22
|
userAgent: request.headers.get("user-agent") ?? undefined,
|
|
24
23
|
cookieHeader,
|
|
25
|
-
body: decoder.decode(body.subarray(0,
|
|
24
|
+
body: decoder.decode(body.subarray(0, request.bodyInspectionLimitBytes)),
|
|
26
25
|
uriPathBytes: simWafByteLength(request.uriPath),
|
|
27
26
|
queryStringBytes: simWafByteLength(request.queryString),
|
|
28
27
|
cookieHeaderBytes: simWafByteLength(cookieHeader),
|
|
@@ -89,7 +89,8 @@ export class SimWafV2 extends SimWafSets {
|
|
|
89
89
|
throw new SimWafNonexistentItemException(`AWS WAF couldn't perform the operation because your resource ` +
|
|
90
90
|
`doesn't exist: web ACL ${evaluation.webAclArn}.`);
|
|
91
91
|
}
|
|
92
|
-
|
|
92
|
+
const limitBytes = webAcl.bodyInspectionLimitBytes(evaluation.resourceType);
|
|
93
|
+
return webAcl.evaluate(simWafInspectedRequest(evaluation.request, evaluation.body, limitBytes));
|
|
93
94
|
}
|
|
94
95
|
/**
|
|
95
96
|
* What the AWS managed rule groups do here, and what a test declares of
|
|
@@ -15,13 +15,13 @@ export type SimWafFieldContent = {
|
|
|
15
15
|
readonly outcome: "noMatch";
|
|
16
16
|
};
|
|
17
17
|
/**
|
|
18
|
-
* How much of a
|
|
18
|
+
* How much of a header set or a cookie set WAF reads.
|
|
19
19
|
*
|
|
20
|
-
* Real WAF stops at 8 KB
|
|
21
|
-
*
|
|
22
|
-
*
|
|
20
|
+
* Real WAF stops at 8 KB and 200 entries for both, whatever the web ACL is in
|
|
21
|
+
* front of. A body is the component whose limit varies by resource type, and
|
|
22
|
+
* `simWafBodyInspectionLimitBytes` carries that one.
|
|
23
23
|
*/
|
|
24
|
-
export declare const
|
|
24
|
+
export declare const simWafHeaderInspectionLimitBytes = 8192;
|
|
25
25
|
/**
|
|
26
26
|
* What a rule does about content too large for WAF to read.
|
|
27
27
|
*/
|
|
@@ -37,5 +37,8 @@ export declare function requiredSimWafOversizeHandling(handling: string | undefi
|
|
|
37
37
|
* settle the statement without looking, and `CONTINUE` inspects as much as WAF
|
|
38
38
|
* would have read: whole entries while they fit, and the first entry cut to
|
|
39
39
|
* the limit when even one does not.
|
|
40
|
+
*
|
|
41
|
+
* The limit is passed in because a body's varies with the resource type the
|
|
42
|
+
* request reached, while a header set and a cookie set are always 8 KB.
|
|
40
43
|
*/
|
|
41
|
-
export declare function simWafFieldContent(candidates: readonly string[], oversizeHandling: SimWafOversizeHandling): SimWafFieldContent;
|
|
44
|
+
export declare function simWafFieldContent(candidates: readonly string[], oversizeHandling: SimWafOversizeHandling, limitBytes: number): SimWafFieldContent;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { invalidSimWafRule } from "./sim-waf-rule-refusals.js";
|
|
2
2
|
/**
|
|
3
|
-
* How much of a
|
|
3
|
+
* How much of a header set or a cookie set WAF reads.
|
|
4
4
|
*
|
|
5
|
-
* Real WAF stops at 8 KB
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Real WAF stops at 8 KB and 200 entries for both, whatever the web ACL is in
|
|
6
|
+
* front of. A body is the component whose limit varies by resource type, and
|
|
7
|
+
* `simWafBodyInspectionLimitBytes` carries that one.
|
|
8
8
|
*/
|
|
9
|
-
export const
|
|
9
|
+
export const simWafHeaderInspectionLimitBytes = 8192;
|
|
10
10
|
const encoder = new TextEncoder();
|
|
11
11
|
const decoder = new TextDecoder();
|
|
12
12
|
/**
|
|
@@ -26,24 +26,30 @@ export function requiredSimWafOversizeHandling(handling, ruleName) {
|
|
|
26
26
|
* settle the statement without looking, and `CONTINUE` inspects as much as WAF
|
|
27
27
|
* would have read: whole entries while they fit, and the first entry cut to
|
|
28
28
|
* the limit when even one does not.
|
|
29
|
+
*
|
|
30
|
+
* The limit is passed in because a body's varies with the resource type the
|
|
31
|
+
* request reached, while a header set and a cookie set are always 8 KB.
|
|
29
32
|
*/
|
|
30
|
-
export function simWafFieldContent(candidates, oversizeHandling) {
|
|
33
|
+
export function simWafFieldContent(candidates, oversizeHandling, limitBytes) {
|
|
31
34
|
const encoded = candidates.map((candidate) => encoder.encode(candidate));
|
|
32
35
|
const total = encoded.reduce((sum, bytes) => sum + bytes.length, 0);
|
|
33
|
-
if (total <=
|
|
36
|
+
if (total <= limitBytes) {
|
|
34
37
|
return { outcome: "inspect", candidates };
|
|
35
38
|
}
|
|
36
39
|
if (oversizeHandling !== "CONTINUE") {
|
|
37
40
|
return { outcome: oversizeHandling === "MATCH" ? "match" : "noMatch" };
|
|
38
41
|
}
|
|
39
|
-
return {
|
|
42
|
+
return {
|
|
43
|
+
outcome: "inspect",
|
|
44
|
+
candidates: readWithinLimit(encoded, limitBytes),
|
|
45
|
+
};
|
|
40
46
|
}
|
|
41
47
|
/**
|
|
42
48
|
* The content WAF would have read before it stopped.
|
|
43
49
|
*/
|
|
44
|
-
function readWithinLimit(encoded) {
|
|
50
|
+
function readWithinLimit(encoded, limitBytes) {
|
|
45
51
|
const read = [];
|
|
46
|
-
let budget =
|
|
52
|
+
let budget = limitBytes;
|
|
47
53
|
for (const bytes of encoded) {
|
|
48
54
|
if (budget === 0) {
|
|
49
55
|
break;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { requiredSimWafOversizeHandling, simWafFieldContent, } from "./sim-waf-field-content.js";
|
|
1
|
+
import { requiredSimWafOversizeHandling, simWafFieldContent, simWafHeaderInspectionLimitBytes, } from "./sim-waf-field-content.js";
|
|
2
2
|
import { requiredSimWafPatternSelection } from "./sim-waf-match-pattern.js";
|
|
3
3
|
import { requiredSimWafMatchScope, simWafCookies, simWafHeaderEntries, simWafPatternCandidates, } from "./sim-waf-request-fields.js";
|
|
4
4
|
import { invalidSimWafRule } from "./sim-waf-rule-refusals.js";
|
|
@@ -22,7 +22,7 @@ export function compileSimWafSetField(field, ruleName) {
|
|
|
22
22
|
invalidSimWafRule(ruleName, "FieldToMatch names no field to match on");
|
|
23
23
|
}
|
|
24
24
|
const oversize = requiredSimWafOversizeHandling(field.Body.OversizeHandling, ruleName);
|
|
25
|
-
return (request) => simWafFieldContent(request.body === undefined ? [] : [decoder.decode(request.body)], oversize);
|
|
25
|
+
return (request) => simWafFieldContent(request.body === undefined ? [] : [decoder.decode(request.body)], oversize, request.bodyInspectionLimitBytes);
|
|
26
26
|
}
|
|
27
27
|
function entriesReader(input, ruleName, entries) {
|
|
28
28
|
const matchScope = requiredSimWafMatchScope(input.MatchScope, ruleName);
|
|
@@ -32,5 +32,5 @@ function entriesReader(input, ruleName, entries) {
|
|
|
32
32
|
entries: entries(request),
|
|
33
33
|
selection,
|
|
34
34
|
matchScope,
|
|
35
|
-
}), oversize);
|
|
35
|
+
}), oversize, simWafHeaderInspectionLimitBytes);
|
|
36
36
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A resource type whose body inspection limit a web ACL can set.
|
|
3
|
+
*
|
|
4
|
+
* Real WAF keys `AssociationConfig.RequestBody` by resource type, and these
|
|
5
|
+
* three are the ones a web ACL goes in front of here. `APP_RUNNER_SERVICE` and
|
|
6
|
+
* `VERIFIED_ACCESS_INSTANCE` are keys AWS takes for resources this simulation
|
|
7
|
+
* has no association for.
|
|
8
|
+
*/
|
|
9
|
+
export type SimWafBodyInspectionResourceType = "CLOUDFRONT" | "API_GATEWAY" | "COGNITO_USER_POOL";
|
|
10
|
+
/**
|
|
11
|
+
* How much of a request body WAF reads for a resource type given no
|
|
12
|
+
* `AssociationConfig`.
|
|
13
|
+
*
|
|
14
|
+
* CloudFront, API Gateway and Cognito all default to 16 KB. A load balancer
|
|
15
|
+
* and an AppSync API are fixed at 8 KB, and a web ACL cannot be put in front
|
|
16
|
+
* of either one here.
|
|
17
|
+
*/
|
|
18
|
+
export declare const simWafBodyInspectionLimitBytes = 16384;
|
|
19
|
+
/**
|
|
20
|
+
* One resource type's entry under `AssociationConfig.RequestBody`.
|
|
21
|
+
*/
|
|
22
|
+
export interface SimWafRequestBodyConfigInput {
|
|
23
|
+
readonly DefaultSizeInspectionLimit?: string | undefined;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The `AssociationConfig` a web ACL is written with.
|
|
27
|
+
*/
|
|
28
|
+
export interface SimWafAssociationConfigInput {
|
|
29
|
+
readonly RequestBody?: Readonly<Record<string, SimWafRequestBodyConfigInput | undefined>> | undefined;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* How much of a request body a web ACL's rules read, by resource type.
|
|
33
|
+
*
|
|
34
|
+
* A web ACL holds one of these whether or not it was written with an
|
|
35
|
+
* `AssociationConfig`, and a resource type the config left out reads the
|
|
36
|
+
* default 16 KB.
|
|
37
|
+
*/
|
|
38
|
+
export declare class SimWafBodyInspectionLimits {
|
|
39
|
+
private readonly limits;
|
|
40
|
+
private constructor();
|
|
41
|
+
/**
|
|
42
|
+
* Read the limits an `AssociationConfig` sets, refusing anything WAF would
|
|
43
|
+
* refuse and anything this simulation cannot apply.
|
|
44
|
+
*/
|
|
45
|
+
static read(input: SimWafAssociationConfigInput | undefined): SimWafBodyInspectionLimits;
|
|
46
|
+
/**
|
|
47
|
+
* How many bytes of a body a request reaching one resource type is inspected
|
|
48
|
+
* for.
|
|
49
|
+
*
|
|
50
|
+
* A request evaluated against a web ACL that no resource holds names no
|
|
51
|
+
* resource type, and reads the default.
|
|
52
|
+
*/
|
|
53
|
+
bytesFor(resourceType: SimWafBodyInspectionResourceType | undefined): number;
|
|
54
|
+
}
|
|
@@ -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.
|
|
@@ -3637,6 +3637,101 @@ unauthorized caller cannot find out which names are taken.
|
|
|
3637
3637
|
in the same way, each against the `dynamodb:` action of its own name. `ListTables` names no table.
|
|
3638
3638
|
It authorizes against `*`.
|
|
3639
3639
|
|
|
3640
|
+
### Fine-grained access control with dynamodb:LeadingKeys
|
|
3641
|
+
|
|
3642
|
+
`GetItem`, `BatchGetItem`, `Query`, `PutItem`, `UpdateItem`, `DeleteItem` and `BatchWriteItem`
|
|
3643
|
+
supply the partition key values they reach as `dynamodb:LeadingKeys`. A policy conditioned on that
|
|
3644
|
+
key restricts a caller to the items under particular partition keys. AWS scopes one user of a
|
|
3645
|
+
shared table to their own rows this way.
|
|
3646
|
+
|
|
3647
|
+
The condition takes the `ForAllValues:` qualifier, as AWS requires for this key. Every partition key
|
|
3648
|
+
value the request reaches has to match. A batch or a write naming one item outside the allowed set
|
|
3649
|
+
is refused whole, and nothing it asked for is applied.
|
|
3650
|
+
|
|
3651
|
+
```typescript sim-dynamodb-leading-keys
|
|
3652
|
+
import { CreateTableCommand, PutItemCommand } from "@aws-sdk/client-dynamodb";
|
|
3653
|
+
import { CreateRoleCommand, PutRolePolicyCommand } from "@aws-sdk/client-iam";
|
|
3654
|
+
import { SimAws } from "@kensio/yulin";
|
|
3655
|
+
|
|
3656
|
+
const simAws = new SimAws();
|
|
3657
|
+
const accountId = simAws.defaultAccountId;
|
|
3658
|
+
const region = simAws.defaultRegionName;
|
|
3659
|
+
|
|
3660
|
+
await simAws.dynamoDb().createTable(
|
|
3661
|
+
new CreateTableCommand({
|
|
3662
|
+
TableName: "OrdersTable",
|
|
3663
|
+
KeySchema: [{ AttributeName: "customerId", KeyType: "HASH" }],
|
|
3664
|
+
AttributeDefinitions: [{ AttributeName: "customerId", AttributeType: "S" }],
|
|
3665
|
+
BillingMode: "PAY_PER_REQUEST",
|
|
3666
|
+
}),
|
|
3667
|
+
);
|
|
3668
|
+
|
|
3669
|
+
const roleCreation = await simAws.iam().createRole(
|
|
3670
|
+
new CreateRoleCommand({
|
|
3671
|
+
RoleName: "CustomerRole",
|
|
3672
|
+
AssumeRolePolicyDocument: JSON.stringify({
|
|
3673
|
+
Version: "2012-10-17",
|
|
3674
|
+
Statement: {
|
|
3675
|
+
Effect: "Allow",
|
|
3676
|
+
Principal: { AWS: `arn:aws:iam::${accountId}:root` },
|
|
3677
|
+
Action: "sts:AssumeRole",
|
|
3678
|
+
},
|
|
3679
|
+
}),
|
|
3680
|
+
}),
|
|
3681
|
+
);
|
|
3682
|
+
|
|
3683
|
+
await simAws.iam().putRolePolicy(
|
|
3684
|
+
new PutRolePolicyCommand({
|
|
3685
|
+
RoleName: "CustomerRole",
|
|
3686
|
+
PolicyName: "OwnItemsOnly",
|
|
3687
|
+
PolicyDocument: JSON.stringify({
|
|
3688
|
+
Version: "2012-10-17",
|
|
3689
|
+
Statement: {
|
|
3690
|
+
Effect: "Allow",
|
|
3691
|
+
Action: "dynamodb:PutItem",
|
|
3692
|
+
Resource: `arn:aws:dynamodb:${region}:${accountId}:table/OrdersTable`,
|
|
3693
|
+
Condition: {
|
|
3694
|
+
"ForAllValues:StringEquals": { "dynamodb:LeadingKeys": ["c-1"] },
|
|
3695
|
+
},
|
|
3696
|
+
},
|
|
3697
|
+
}),
|
|
3698
|
+
}),
|
|
3699
|
+
);
|
|
3700
|
+
|
|
3701
|
+
const caller = { kind: "arn", arn: roleCreation.Role.Arn } as const;
|
|
3702
|
+
|
|
3703
|
+
// The customer's own item is written.
|
|
3704
|
+
await simAws.dynamoDb().putItem(
|
|
3705
|
+
new PutItemCommand({
|
|
3706
|
+
TableName: "OrdersTable",
|
|
3707
|
+
Item: { customerId: { S: "c-1" }, total: { N: "25" } },
|
|
3708
|
+
}),
|
|
3709
|
+
{ caller },
|
|
3710
|
+
);
|
|
3711
|
+
|
|
3712
|
+
// Another customer's item is refused.
|
|
3713
|
+
try {
|
|
3714
|
+
await simAws.dynamoDb().putItem(
|
|
3715
|
+
new PutItemCommand({
|
|
3716
|
+
TableName: "OrdersTable",
|
|
3717
|
+
Item: { customerId: { S: "c-2" }, total: { N: "25" } },
|
|
3718
|
+
}),
|
|
3719
|
+
{ caller },
|
|
3720
|
+
);
|
|
3721
|
+
} catch (error) {
|
|
3722
|
+
console.log(error instanceof Error ? error.name : "unknown error");
|
|
3723
|
+
// "AccessDenied"
|
|
3724
|
+
}
|
|
3725
|
+
```
|
|
3726
|
+
|
|
3727
|
+
Reading the partition key values needs the table's key schema. The table is found first for that,
|
|
3728
|
+
before the caller is authorized against it. A caller with no permission still hears `AccessDenied`
|
|
3729
|
+
when the table is missing, and an unauthorized caller cannot find out which table names are taken.
|
|
3730
|
+
|
|
3731
|
+
A request carrying no readable partition key value leaves the key out of the condition context. A
|
|
3732
|
+
`ForAllValues:` condition matches a request carrying no value for its key, as it does on AWS. The
|
|
3733
|
+
condition allows such a request.
|
|
3734
|
+
|
|
3640
3735
|
A transaction is authorized as the operations it is made of rather than as itself. Each action of a
|
|
3641
3736
|
`TransactWriteItems` needs `dynamodb:PutItem`, `dynamodb:UpdateItem`, `dynamodb:DeleteItem` or
|
|
3642
3737
|
`dynamodb:ConditionCheckItem` against the table it names, and each `Get` of a `TransactGetItems`
|
|
@@ -3657,6 +3752,9 @@ is written.
|
|
|
3657
3752
|
answering with the attributes it projects, and paging with a `LastEvaluatedKey` carrying the index
|
|
3658
3753
|
key and the table key together. A local secondary index also answers a strongly consistent read,
|
|
3659
3754
|
and fetches an unprojected attribute from the base table.
|
|
3755
|
+
- `dynamodb:LeadingKeys` on `GetItem`, `BatchGetItem`, `Query`, `PutItem`, `UpdateItem`,
|
|
3756
|
+
`DeleteItem` and `BatchWriteItem`, carrying the partition key values the request reaches so a
|
|
3757
|
+
`ForAllValues:` condition can scope a caller to particular items.
|
|
3660
3758
|
- `DescribeTable`, answering with the full table description, by table name or ARN.
|
|
3661
3759
|
- `ListTables`, ordered by UTF-8 bytes and paged with `Limit` and `ExclusiveStartTableName`.
|
|
3662
3760
|
- `DeleteTable`, following the table status DynamoDB moves a deleted table through, and refusing a
|
|
@@ -3730,6 +3828,16 @@ Arn`, `Fn::GetAtt … StreamArn` and `Fn::GetAtt … TableId` answering. A CDK `
|
|
|
3730
3828
|
|
|
3731
3829
|
## Limitations
|
|
3732
3830
|
|
|
3831
|
+
- `dynamodb:LeadingKeys` is the only DynamoDB condition key supplied. `dynamodb:Attributes`,
|
|
3832
|
+
`dynamodb:Select` and `dynamodb:ReturnValues` are absent from the condition context, and a
|
|
3833
|
+
statement conditioned on one of them matches no request. The transactional operations supply no
|
|
3834
|
+
leading keys either, and are authorized by action and table alone.
|
|
3835
|
+
- A partition key value reaches a policy condition as a string for a `S` key and as its digits for
|
|
3836
|
+
an `N` key. A binary partition key has no documented form for the condition key. A request
|
|
3837
|
+
carrying one leaves the value out.
|
|
3838
|
+
- A request against a table that is not there is authorized carrying no leading keys, since there is
|
|
3839
|
+
no key schema to read them with. A `ForAllValues:` condition matches such a request. A caller
|
|
3840
|
+
barred only by that condition learns the table is missing.
|
|
3733
3841
|
- The document client's PartiQL Commands go unconverted, because PartiQL is an operation this
|
|
3734
3842
|
simulation lacks yet. `ExecuteStatementCommand`, `BatchExecuteStatementCommand` and
|
|
3735
3843
|
`ExecuteTransactionCommand` are refused by name, never half converted.
|
|
@@ -335,6 +335,11 @@ matches instead, as AWS documents. With no value in the request there is none fo
|
|
|
335
335
|
to equal. A `ForAnyValue:` operator answers false for an absent key whatever it wraps, because no
|
|
336
336
|
request value is there to satisfy it.
|
|
337
337
|
|
|
338
|
+
A `ForAllValues:` operator answers true for an absent key, which is also AWS behaviour. Every value
|
|
339
|
+
the request carries matches when it carries none. AWS warns that this leaves a `ForAllValues:`
|
|
340
|
+
`Allow` overly permissive, and asks for a `Null` check with a `false` value beside it. `Null` is
|
|
341
|
+
absent from the operators above. A statement written that way fails closed here.
|
|
342
|
+
|
|
338
343
|
### Statements left unevaluated
|
|
339
344
|
|
|
340
345
|
An operator from outside the list above fails closed. The statement holding it matches nothing, and
|
|
@@ -1684,8 +1689,9 @@ Sim IAM models the policy behaviour that multi-service tests most commonly need.
|
|
|
1684
1689
|
- Only the condition operators listed above are supported. A statement using any other operator
|
|
1685
1690
|
fails closed, matching no request. `decision.unevaluatedStatements` names those statements, and a
|
|
1686
1691
|
test can assert that a decision was reached over policies read in full
|
|
1687
|
-
- A positive `ForAllValues:` condition fails to match
|
|
1688
|
-
|
|
1692
|
+
- A positive `ForAllValues:` condition fails to match an empty value set, where AWS matches one. An
|
|
1693
|
+
absent context key matches, as it does on AWS. A service here leaves a key out where the request
|
|
1694
|
+
carries no value for it, and the empty set is reached only by a caller passing one to `authorize`
|
|
1689
1695
|
- Signature age is deliberately not enforced. `X-Amz-Date` must be present, well formed, and agree
|
|
1690
1696
|
with the credential scope date, but is never compared to a clock. A client stamping real time can
|
|
1691
1697
|
therefore reach a simulation keeping a different one. Session expiry _is_ enforced, against
|
|
@@ -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
|
|