@kensio/yulin 1.22.0 → 1.23.1
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/controller/sim-service-controller.d.ts +6 -0
- package/dist/serve/controller/sim-service-controller.js +7 -0
- package/dist/serve/http/request/sim-aws-request-authenticator.js +3 -0
- package/dist/service/apigateway/serve/sim-api-gateway-controller.js +1 -0
- package/dist/service/apigateway/serve/sim-rest-api-web-acl-inspection.d.ts +2 -0
- package/dist/service/apigateway/serve/sim-rest-api-web-acl-inspection.js +1 -0
- package/dist/service/cloudfront/controller/dependency/sim-cf-controller-dependency.js +6 -3
- package/dist/service/cloudfront/controller/sim-cloudfront-request-pipeline.js +2 -1
- package/dist/service/cloudfront/controller/web-acl/sim-cf-web-acl-guard.d.ts +25 -3
- package/dist/service/cloudfront/controller/web-acl/sim-cf-web-acl-guard.js +35 -6
- package/dist/service/cognito/serve/sim-cognito-controller.js +4 -3
- package/dist/service/cognito/serve/sim-cognito-domain-controller.js +2 -0
- package/dist/service/cognito/serve/sim-cognito-web-acl-inspection.d.ts +2 -0
- package/dist/service/cognito/serve/sim-cognito-web-acl-inspection.js +1 -0
- package/dist/service/iam/index.d.ts +1 -0
- package/dist/service/iam/index.js +1 -0
- package/dist/service/iam/request/error/sim-aws-request-auth.error.d.ts +1 -1
- package/dist/service/iam/request/error/sim-aws-request-auth.error.js +1 -1
- package/dist/service/iam/request/sim-aws-control-headers.d.ts +2 -1
- package/dist/service/iam/request/sim-aws-control-headers.js +4 -1
- package/dist/service/iam/request/sim-aws-source-ip-header.d.ts +19 -0
- package/dist/service/iam/request/sim-aws-source-ip-header.js +32 -0
- package/dist/service/wafv2/association/sim-waf-associations.js +1 -1
- package/dist/service/wafv2/association/sim-waf-protection.d.ts +5 -0
- package/dist/service/wafv2/association/sim-waf-request-inspection.d.ts +2 -0
- package/dist/service/wafv2/association/sim-waf-request-inspection.js +7 -2
- package/dist/service/wafv2/evaluate/sim-waf-blocked-response.d.ts +12 -0
- package/dist/service/wafv2/evaluate/sim-waf-blocked-response.js +1 -0
- package/dist/service/wafv2/evaluate/sim-waf-evaluation-request.d.ts +5 -0
- package/dist/service/wafv2/evaluate/sim-waf-inspected-request.d.ts +12 -1
- package/dist/service/wafv2/evaluate/sim-waf-inspected-request.js +12 -1
- package/dist/service/wafv2/index.d.ts +2 -1
- package/dist/service/wafv2/index.js +1 -0
- package/dist/service/wafv2/managed/group/sim-waf-ip-reputation.d.ts +8 -5
- package/dist/service/wafv2/managed/group/sim-waf-ip-reputation.js +10 -5
- package/dist/service/wafv2/managed/sim-waf-managed-address-declaration.d.ts +32 -0
- package/dist/service/wafv2/managed/sim-waf-managed-address-declaration.js +52 -0
- package/dist/service/wafv2/managed/sim-waf-managed-address-range.d.ts +21 -0
- package/dist/service/wafv2/managed/sim-waf-managed-address-range.js +73 -0
- package/dist/service/wafv2/managed/sim-waf-managed-rule-groups.d.ts +7 -0
- package/dist/service/wafv2/managed/sim-waf-managed-rule-groups.js +15 -0
- package/dist/service/wafv2/managed/sim-waf-managed-rule.type.d.ts +5 -0
- package/dist/service/wafv2/managed/sim-waf-managed-rules.d.ts +27 -1
- package/dist/service/wafv2/managed/sim-waf-managed-rules.js +33 -13
- package/dist/service/wafv2/sim-wafv2.js +2 -1
- package/dist/service/wafv2/statement/sim-waf-rate-based-input.d.ts +5 -4
- package/dist/service/wafv2/statement/sim-waf-rate-based-input.js +6 -6
- package/dist/service/wafv2/web-acl/sim-waf-action-input.js +1 -0
- package/docs/services/cloudfront/README.md +17 -1
- package/docs/services/iam/README.md +6 -0
- package/docs/services/wafv2/README.md +112 -15
- package/package.json +3 -3
|
@@ -3,16 +3,19 @@ import type { SimWafManagedRuleGroupDefinition } from "../sim-waf-managed-rule.t
|
|
|
3
3
|
* The Amazon IP reputation list, which decides by the address a request came
|
|
4
4
|
* from.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* acts on
|
|
6
|
+
* AWS publishes none of the addresses on its lists, so every rule here is
|
|
7
|
+
* declared-only. A test says which addresses a rule flags with
|
|
8
|
+
* `managedRules().onAddress`, and sends a request from one of them with the
|
|
9
|
+
* `x-sim-aws-source-ip` header. The group then labels and acts on it as AWS
|
|
10
|
+
* documents. `managedRules().onRequest` declares a match by path instead.
|
|
10
11
|
*/
|
|
11
12
|
export declare const simWafIpReputationList: SimWafManagedRuleGroupDefinition;
|
|
12
13
|
/**
|
|
13
14
|
* The anonymous IP list, for VPNs, proxies, Tor nodes and hosting providers.
|
|
14
15
|
*
|
|
15
16
|
* It decides by source address as the reputation list does, and every rule is
|
|
16
|
-
* declared-only for the same reason.
|
|
17
|
+
* declared-only for the same reason. `AnonymousIPList` stands for the VPNs,
|
|
18
|
+
* proxies and Tor nodes, and `HostingProviderIPList` for the hosting and cloud
|
|
19
|
+
* providers other than AWS.
|
|
17
20
|
*/
|
|
18
21
|
export declare const simWafAnonymousIpList: SimWafManagedRuleGroupDefinition;
|
|
@@ -2,15 +2,17 @@
|
|
|
2
2
|
* The Amazon IP reputation list, which decides by the address a request came
|
|
3
3
|
* from.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* acts on
|
|
5
|
+
* AWS publishes none of the addresses on its lists, so every rule here is
|
|
6
|
+
* declared-only. A test says which addresses a rule flags with
|
|
7
|
+
* `managedRules().onAddress`, and sends a request from one of them with the
|
|
8
|
+
* `x-sim-aws-source-ip` header. The group then labels and acts on it as AWS
|
|
9
|
+
* documents. `managedRules().onRequest` declares a match by path instead.
|
|
9
10
|
*/
|
|
10
11
|
export const simWafIpReputationList = {
|
|
11
12
|
name: "AWSManagedRulesAmazonIpReputationList",
|
|
12
13
|
labelNamespace: "awswaf:managed:aws:amazon-ip-list",
|
|
13
14
|
capacity: 25,
|
|
15
|
+
readsSourceAddress: true,
|
|
14
16
|
rules: [
|
|
15
17
|
{
|
|
16
18
|
name: "AWSManagedIPReputationList",
|
|
@@ -34,12 +36,15 @@ export const simWafIpReputationList = {
|
|
|
34
36
|
* The anonymous IP list, for VPNs, proxies, Tor nodes and hosting providers.
|
|
35
37
|
*
|
|
36
38
|
* It decides by source address as the reputation list does, and every rule is
|
|
37
|
-
* declared-only for the same reason.
|
|
39
|
+
* declared-only for the same reason. `AnonymousIPList` stands for the VPNs,
|
|
40
|
+
* proxies and Tor nodes, and `HostingProviderIPList` for the hosting and cloud
|
|
41
|
+
* providers other than AWS.
|
|
38
42
|
*/
|
|
39
43
|
export const simWafAnonymousIpList = {
|
|
40
44
|
name: "AWSManagedRulesAnonymousIpList",
|
|
41
45
|
labelNamespace: "awswaf:managed:aws:anonymous-ip-list",
|
|
42
46
|
capacity: 50,
|
|
47
|
+
readsSourceAddress: true,
|
|
43
48
|
rules: [
|
|
44
49
|
{ name: "AnonymousIPList", label: "AnonymousIPList", tier: "declared" },
|
|
45
50
|
{
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { SimWafDeclared } from "./sim-waf-managed-declaration.js";
|
|
2
|
+
/**
|
|
3
|
+
* The managed rules a test says flag the requests from some addresses.
|
|
4
|
+
*/
|
|
5
|
+
export interface SimWafManagedAddressDeclaration {
|
|
6
|
+
/**
|
|
7
|
+
* The rules that flag the addresses, by the name AWS gives them, such as
|
|
8
|
+
* `AnonymousIPList` or `HostingProviderIPList`.
|
|
9
|
+
*/
|
|
10
|
+
readonly matches: readonly string[];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The address ranges a test declared, each with the rules that flag a request
|
|
14
|
+
* from inside it.
|
|
15
|
+
*
|
|
16
|
+
* Declaring the same range again replaces what it was declared with, and a
|
|
17
|
+
* request from inside two declared ranges is flagged by the rules of both.
|
|
18
|
+
*/
|
|
19
|
+
export declare class SimWafDeclaredAddresses {
|
|
20
|
+
#private;
|
|
21
|
+
/**
|
|
22
|
+
* Declare the rules that flag the requests from one address or CIDR range.
|
|
23
|
+
*
|
|
24
|
+
* Each rule is checked to be one that decides by the client address.
|
|
25
|
+
*/
|
|
26
|
+
declare(range: string, ruleNames: readonly string[]): void;
|
|
27
|
+
/**
|
|
28
|
+
* What was declared for a request's path, joined with what was declared
|
|
29
|
+
* for the address it came from.
|
|
30
|
+
*/
|
|
31
|
+
joinedWith(declared: SimWafDeclared, sourceIp: string): SimWafDeclared;
|
|
32
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { SimWafDeclarationError } from "../error/sim-wafv2.error.js";
|
|
2
|
+
import { SimWafDeclaredAddressRange } from "./sim-waf-managed-address-range.js";
|
|
3
|
+
import { requiredSimWafManagedRule, } from "./sim-waf-managed-rule-groups.js";
|
|
4
|
+
/**
|
|
5
|
+
* The address ranges a test declared, each with the rules that flag a request
|
|
6
|
+
* from inside it.
|
|
7
|
+
*
|
|
8
|
+
* Declaring the same range again replaces what it was declared with, and a
|
|
9
|
+
* request from inside two declared ranges is flagged by the rules of both.
|
|
10
|
+
*/
|
|
11
|
+
export class SimWafDeclaredAddresses {
|
|
12
|
+
#byRange = new Map();
|
|
13
|
+
/**
|
|
14
|
+
* Declare the rules that flag the requests from one address or CIDR range.
|
|
15
|
+
*
|
|
16
|
+
* Each rule is checked to be one that decides by the client address.
|
|
17
|
+
*/
|
|
18
|
+
declare(range, ruleNames) {
|
|
19
|
+
const matches = new Set(ruleNames.map((name) => simWafAddressRuleName(requiredSimWafManagedRule(name))));
|
|
20
|
+
this.#byRange.set(range.trim(), SimWafDeclaredAddressRange.read(range, matches));
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* What was declared for a request's path, joined with what was declared
|
|
24
|
+
* for the address it came from.
|
|
25
|
+
*/
|
|
26
|
+
joinedWith(declared, sourceIp) {
|
|
27
|
+
const byAddress = this.#byRange
|
|
28
|
+
.values()
|
|
29
|
+
.filter((range) => range.holds(sourceIp))
|
|
30
|
+
.flatMap((range) => range.matches)
|
|
31
|
+
.toArray();
|
|
32
|
+
if (byAddress.length === 0) {
|
|
33
|
+
return declared;
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
matches: new Set([...declared.matches, ...byAddress]),
|
|
37
|
+
groupLabels: declared.groupLabels,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The name of a rule a test may declare by address, refusing one whose group
|
|
43
|
+
* reads the request rather than where it came from.
|
|
44
|
+
*/
|
|
45
|
+
function simWafAddressRuleName(entry) {
|
|
46
|
+
if (entry.group.readsSourceAddress !== true) {
|
|
47
|
+
throw new SimWafDeclarationError(`${entry.rule.name} belongs to ${entry.group.name}, which does not ` +
|
|
48
|
+
`decide by the client address, so a match for it is declared by path ` +
|
|
49
|
+
`with managedRules().onRequest rather than by address.`);
|
|
50
|
+
}
|
|
51
|
+
return entry.rule.name;
|
|
52
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One address or range of addresses, and the rules that flag a request from
|
|
3
|
+
* inside it.
|
|
4
|
+
*/
|
|
5
|
+
export declare class SimWafDeclaredAddressRange {
|
|
6
|
+
#private;
|
|
7
|
+
readonly matches: ReadonlySet<string>;
|
|
8
|
+
private constructor();
|
|
9
|
+
/**
|
|
10
|
+
* Read one range a test declared, refusing what is not an address or a CIDR
|
|
11
|
+
* range.
|
|
12
|
+
*/
|
|
13
|
+
static read(range: string, matches: ReadonlySet<string>): SimWafDeclaredAddressRange;
|
|
14
|
+
/**
|
|
15
|
+
* Whether one client address falls inside this range.
|
|
16
|
+
*
|
|
17
|
+
* An IPv4 address is never inside an IPv6 range, nor the reverse, which is
|
|
18
|
+
* how an AWS IP set holding one kind treats the other.
|
|
19
|
+
*/
|
|
20
|
+
holds(sourceIp: string): boolean;
|
|
21
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { BlockList, isIP } from "node:net";
|
|
2
|
+
import { SimWafDeclarationError } from "../error/sim-wafv2.error.js";
|
|
3
|
+
/**
|
|
4
|
+
* One address or range of addresses, and the rules that flag a request from
|
|
5
|
+
* inside it.
|
|
6
|
+
*/
|
|
7
|
+
export class SimWafDeclaredAddressRange {
|
|
8
|
+
matches;
|
|
9
|
+
#addresses = new BlockList();
|
|
10
|
+
constructor(range, matches) {
|
|
11
|
+
this.matches = matches;
|
|
12
|
+
if (range.prefix === undefined) {
|
|
13
|
+
this.#addresses.addAddress(range.address, range.family);
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
this.#addresses.addSubnet(range.address, range.prefix, range.family);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Read one range a test declared, refusing what is not an address or a CIDR
|
|
21
|
+
* range.
|
|
22
|
+
*/
|
|
23
|
+
static read(range, matches) {
|
|
24
|
+
return new SimWafDeclaredAddressRange(checkedRange(range), matches);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Whether one client address falls inside this range.
|
|
28
|
+
*
|
|
29
|
+
* An IPv4 address is never inside an IPv6 range, nor the reverse, which is
|
|
30
|
+
* how an AWS IP set holding one kind treats the other.
|
|
31
|
+
*/
|
|
32
|
+
holds(sourceIp) {
|
|
33
|
+
const family = familyOf(sourceIp);
|
|
34
|
+
return family !== undefined && this.#addresses.check(sourceIp, family);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Read an address or a CIDR range into the address, the prefix length and the
|
|
39
|
+
* family.
|
|
40
|
+
*/
|
|
41
|
+
function checkedRange(range) {
|
|
42
|
+
const [address = "", prefixText, ...rest] = range.trim().split("/");
|
|
43
|
+
const family = familyOf(address);
|
|
44
|
+
if (family === undefined || rest.length > 0) {
|
|
45
|
+
throw invalidRange(range);
|
|
46
|
+
}
|
|
47
|
+
if (prefixText === undefined) {
|
|
48
|
+
return { address, prefix: undefined, family };
|
|
49
|
+
}
|
|
50
|
+
const prefix = Number(prefixText);
|
|
51
|
+
const widest = family === "ipv4" ? 32 : 128;
|
|
52
|
+
if (!/^\d+$/u.test(prefixText) || prefix > widest) {
|
|
53
|
+
throw invalidRange(range);
|
|
54
|
+
}
|
|
55
|
+
return { address, prefix, family };
|
|
56
|
+
}
|
|
57
|
+
function familyOf(address) {
|
|
58
|
+
switch (isIP(address)) {
|
|
59
|
+
case 4: {
|
|
60
|
+
return "ipv4";
|
|
61
|
+
}
|
|
62
|
+
case 6: {
|
|
63
|
+
return "ipv6";
|
|
64
|
+
}
|
|
65
|
+
default: {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function invalidRange(range) {
|
|
71
|
+
return new SimWafDeclarationError(`A declared address match needs one IPv4 or IPv6 address, or a CIDR ` +
|
|
72
|
+
`range such as 185.220.101.0/24, and ${range} is neither`);
|
|
73
|
+
}
|
|
@@ -30,6 +30,13 @@ export declare function simWafManagedRuleGroupNames(): readonly string[];
|
|
|
30
30
|
* declared match and an action override name a rule and nothing else.
|
|
31
31
|
*/
|
|
32
32
|
export declare function findSimWafManagedRule(name: string): SimWafManagedRuleEntry | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Find one managed rule by name, refusing a name no simulated group holds.
|
|
35
|
+
*
|
|
36
|
+
* A declaration naming a rule that is absent is refused where it is made,
|
|
37
|
+
* since it would otherwise sit there matching nothing.
|
|
38
|
+
*/
|
|
39
|
+
export declare function requiredSimWafManagedRule(name: string): SimWafManagedRuleEntry;
|
|
33
40
|
/**
|
|
34
41
|
* Every rule of every simulated group, in the order the groups evaluate them.
|
|
35
42
|
*/
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SimWafDeclarationError } from "../error/sim-wafv2.error.js";
|
|
1
2
|
import { simWafAdminProtectionRuleSet } from "./group/sim-waf-admin-protection.js";
|
|
2
3
|
import { simWafBotControlRuleSet } from "./group/sim-waf-bot-control.js";
|
|
3
4
|
import { simWafCommonRuleSet } from "./group/sim-waf-common-rule-set.js";
|
|
@@ -45,6 +46,20 @@ export function simWafManagedRuleGroupNames() {
|
|
|
45
46
|
export function findSimWafManagedRule(name) {
|
|
46
47
|
return rulesByName.get(name);
|
|
47
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Find one managed rule by name, refusing a name no simulated group holds.
|
|
51
|
+
*
|
|
52
|
+
* A declaration naming a rule that is absent is refused where it is made,
|
|
53
|
+
* since it would otherwise sit there matching nothing.
|
|
54
|
+
*/
|
|
55
|
+
export function requiredSimWafManagedRule(name) {
|
|
56
|
+
const entry = rulesByName.get(name);
|
|
57
|
+
if (entry === undefined) {
|
|
58
|
+
throw new SimWafDeclarationError(`No simulated AWS managed rule group holds a rule named ${name}. The ` +
|
|
59
|
+
`rules that are simulated are reported by managedRules().rules().`);
|
|
60
|
+
}
|
|
61
|
+
return entry;
|
|
62
|
+
}
|
|
48
63
|
/**
|
|
49
64
|
* Every rule of every simulated group, in the order the groups evaluate them.
|
|
50
65
|
*/
|
|
@@ -52,5 +52,10 @@ export interface SimWafManagedRuleGroupDefinition {
|
|
|
52
52
|
readonly labelNamespace: string;
|
|
53
53
|
/** The WCUs the group costs, which `DescribeManagedRuleGroup` reports. */
|
|
54
54
|
readonly capacity: number;
|
|
55
|
+
/**
|
|
56
|
+
* Whether the group decides by the client address alone, which is what lets
|
|
57
|
+
* a test declare its matches by address with `managedRules().onAddress`.
|
|
58
|
+
*/
|
|
59
|
+
readonly readsSourceAddress?: boolean | undefined;
|
|
55
60
|
readonly rules: readonly SimWafManagedRuleDefinition[];
|
|
56
61
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { SimWafInspectedRequest } from "../evaluate/sim-waf-inspected-request.js";
|
|
2
2
|
import { type SimWafManagedRuleReport } from "./sim-waf-managed-rule-report.js";
|
|
3
3
|
import type { SimWafManagedRuleTier } from "./sim-waf-managed-rule.type.js";
|
|
4
|
+
import { type SimWafManagedAddressDeclaration } from "./sim-waf-managed-address-declaration.js";
|
|
4
5
|
import { type SimWafDeclared, type SimWafManagedMatchDeclaration } from "./sim-waf-managed-declaration.js";
|
|
5
6
|
export type { SimWafDeclared, SimWafManagedMatchDeclaration, } from "./sim-waf-managed-declaration.js";
|
|
7
|
+
export type { SimWafManagedAddressDeclaration } from "./sim-waf-managed-address-declaration.js";
|
|
6
8
|
/**
|
|
7
9
|
* The managed rule matches this simulated WAFv2 answers with, and what it
|
|
8
10
|
* covers of each rule.
|
|
@@ -28,6 +30,16 @@ export type { SimWafDeclared, SimWafManagedMatchDeclaration, } from "./sim-waf-m
|
|
|
28
30
|
* });
|
|
29
31
|
* ```
|
|
30
32
|
*
|
|
33
|
+
* The IP reputation and anonymous IP groups decide by the client address, so
|
|
34
|
+
* their matches can be declared by address instead, for a request that states
|
|
35
|
+
* where it came from with the `x-sim-aws-source-ip` header:
|
|
36
|
+
*
|
|
37
|
+
* ```typescript
|
|
38
|
+
* simAws.wafV2().managedRules().onAddress("185.220.101.0/24", {
|
|
39
|
+
* matches: ["AnonymousIPList"],
|
|
40
|
+
* });
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
31
43
|
* A declared match is a match, and everything after it is what the group would
|
|
32
44
|
* have done anyway: the rule adds its label, an action override applies to it,
|
|
33
45
|
* and the group blocks by that rule.
|
|
@@ -42,9 +54,24 @@ export declare class SimWafManagedRules {
|
|
|
42
54
|
* is refused here rather than matching nothing later.
|
|
43
55
|
*/
|
|
44
56
|
onRequest(uriPath: string, declaration: SimWafManagedMatchDeclaration): void;
|
|
57
|
+
/**
|
|
58
|
+
* Declare which managed rules flag the requests from one address or one
|
|
59
|
+
* CIDR range of addresses.
|
|
60
|
+
*
|
|
61
|
+
* Only the rules of a group that decides by the client address can be
|
|
62
|
+
* declared this way, which is the IP reputation list and the anonymous IP
|
|
63
|
+
* list. A request states its address with the `x-sim-aws-source-ip` header,
|
|
64
|
+
* and one that states none comes from `127.0.0.1`.
|
|
65
|
+
*
|
|
66
|
+
* Declaring the same range again replaces it.
|
|
67
|
+
*/
|
|
68
|
+
onAddress(range: string, declaration: SimWafManagedAddressDeclaration): void;
|
|
45
69
|
/**
|
|
46
70
|
* What a test declared about one request: the rules that claim it, and the
|
|
47
71
|
* labels a group adds to it whatever its rules decide.
|
|
72
|
+
*
|
|
73
|
+
* A request is claimed by what was declared for its path and for its
|
|
74
|
+
* address together.
|
|
48
75
|
*/
|
|
49
76
|
declared(request: SimWafInspectedRequest): SimWafDeclared;
|
|
50
77
|
/**
|
|
@@ -59,5 +86,4 @@ export declare class SimWafManagedRules {
|
|
|
59
86
|
* AWS rule they stand for.
|
|
60
87
|
*/
|
|
61
88
|
rules(): readonly SimWafManagedRuleReport[];
|
|
62
|
-
private requiredRule;
|
|
63
89
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { SimWafDeclarationError } from "../error/sim-wafv2.error.js";
|
|
2
|
-
import {
|
|
2
|
+
import { requiredSimWafManagedRule } from "./sim-waf-managed-rule-groups.js";
|
|
3
3
|
import { simWafManagedRuleReports, } from "./sim-waf-managed-rule-report.js";
|
|
4
|
+
import { SimWafDeclaredAddresses, } from "./sim-waf-managed-address-declaration.js";
|
|
4
5
|
import { simWafNothingDeclared, simWafReadDeclaration, } from "./sim-waf-managed-declaration.js";
|
|
5
6
|
/**
|
|
6
7
|
* The managed rule matches this simulated WAFv2 answers with, and what it
|
|
@@ -27,12 +28,23 @@ import { simWafNothingDeclared, simWafReadDeclaration, } from "./sim-waf-managed
|
|
|
27
28
|
* });
|
|
28
29
|
* ```
|
|
29
30
|
*
|
|
31
|
+
* The IP reputation and anonymous IP groups decide by the client address, so
|
|
32
|
+
* their matches can be declared by address instead, for a request that states
|
|
33
|
+
* where it came from with the `x-sim-aws-source-ip` header:
|
|
34
|
+
*
|
|
35
|
+
* ```typescript
|
|
36
|
+
* simAws.wafV2().managedRules().onAddress("185.220.101.0/24", {
|
|
37
|
+
* matches: ["AnonymousIPList"],
|
|
38
|
+
* });
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
30
41
|
* A declared match is a match, and everything after it is what the group would
|
|
31
42
|
* have done anyway: the rule adds its label, an action override applies to it,
|
|
32
43
|
* and the group blocks by that rule.
|
|
33
44
|
*/
|
|
34
45
|
export class SimWafManagedRules {
|
|
35
46
|
#byUriPath = new Map();
|
|
47
|
+
#byAddress = new SimWafDeclaredAddresses();
|
|
36
48
|
/**
|
|
37
49
|
* Declare which managed rules claim a request to one URI path.
|
|
38
50
|
*
|
|
@@ -45,20 +57,37 @@ export class SimWafManagedRules {
|
|
|
45
57
|
throw new SimWafDeclarationError("A declared managed rule match needs the URI path of the request it " +
|
|
46
58
|
"is declared for");
|
|
47
59
|
}
|
|
48
|
-
this.#byUriPath.set(uriPath, simWafReadDeclaration(declaration, (name) =>
|
|
60
|
+
this.#byUriPath.set(uriPath, simWafReadDeclaration(declaration, (name) => requiredSimWafManagedRule(name).rule.name));
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Declare which managed rules flag the requests from one address or one
|
|
64
|
+
* CIDR range of addresses.
|
|
65
|
+
*
|
|
66
|
+
* Only the rules of a group that decides by the client address can be
|
|
67
|
+
* declared this way, which is the IP reputation list and the anonymous IP
|
|
68
|
+
* list. A request states its address with the `x-sim-aws-source-ip` header,
|
|
69
|
+
* and one that states none comes from `127.0.0.1`.
|
|
70
|
+
*
|
|
71
|
+
* Declaring the same range again replaces it.
|
|
72
|
+
*/
|
|
73
|
+
onAddress(range, declaration) {
|
|
74
|
+
this.#byAddress.declare(range, declaration.matches);
|
|
49
75
|
}
|
|
50
76
|
/**
|
|
51
77
|
* What a test declared about one request: the rules that claim it, and the
|
|
52
78
|
* labels a group adds to it whatever its rules decide.
|
|
79
|
+
*
|
|
80
|
+
* A request is claimed by what was declared for its path and for its
|
|
81
|
+
* address together.
|
|
53
82
|
*/
|
|
54
83
|
declared(request) {
|
|
55
|
-
return this.#byUriPath.get(request.uriPath) ?? simWafNothingDeclared;
|
|
84
|
+
return this.#byAddress.joinedWith(this.#byUriPath.get(request.uriPath) ?? simWafNothingDeclared, request.sourceIp);
|
|
56
85
|
}
|
|
57
86
|
/**
|
|
58
87
|
* What this simulation covers of one managed rule.
|
|
59
88
|
*/
|
|
60
89
|
tierOf(ruleName) {
|
|
61
|
-
return
|
|
90
|
+
return requiredSimWafManagedRule(ruleName).rule.tier;
|
|
62
91
|
}
|
|
63
92
|
/**
|
|
64
93
|
* What this simulation covers of every managed rule it carries.
|
|
@@ -70,13 +99,4 @@ export class SimWafManagedRules {
|
|
|
70
99
|
rules() {
|
|
71
100
|
return simWafManagedRuleReports();
|
|
72
101
|
}
|
|
73
|
-
requiredRule(ruleName) {
|
|
74
|
-
const entry = findSimWafManagedRule(ruleName);
|
|
75
|
-
if (entry === undefined) {
|
|
76
|
-
throw new SimWafDeclarationError(`No simulated AWS managed rule group holds a rule named ` +
|
|
77
|
-
`${ruleName}. The rules that are simulated are reported by ` +
|
|
78
|
-
`managedRules().rules().`);
|
|
79
|
-
}
|
|
80
|
-
return entry;
|
|
81
|
-
}
|
|
82
102
|
}
|
|
@@ -98,7 +98,8 @@ export class SimWafV2 extends SimWafSets {
|
|
|
98
98
|
evaluateRequest(evaluation) {
|
|
99
99
|
const webAcl = this.requireWebAcl(evaluation.webAclArn);
|
|
100
100
|
const limitBytes = webAcl.bodyInspectionLimitBytes(evaluation.resourceType);
|
|
101
|
-
|
|
101
|
+
const { request, body, sourceIp } = evaluation;
|
|
102
|
+
return webAcl.evaluate(simWafInspectedRequest(request, body, limitBytes, sourceIp));
|
|
102
103
|
}
|
|
103
104
|
/**
|
|
104
105
|
* What the AWS managed rule groups do here, and what a test declares of
|
|
@@ -36,10 +36,11 @@ export declare function simWafRateWindowMilliseconds(statement: SimWafRateBasedS
|
|
|
36
36
|
/**
|
|
37
37
|
* Read how a rate-based statement groups the requests it counts.
|
|
38
38
|
*
|
|
39
|
-
* `IP` counts each client address on its own.
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
39
|
+
* `IP` counts each client address on its own. A request comes from
|
|
40
|
+
* `127.0.0.1` unless it states another address with `x-sim-aws-source-ip`, so
|
|
41
|
+
* a test that states none has one client for the whole simulation. That is
|
|
42
|
+
* the case a rate limiting test is usually written about: one client, sending
|
|
43
|
+
* until the rule trips.
|
|
43
44
|
*
|
|
44
45
|
* `CONSTANT` counts every request the statement sees together, and AWS asks
|
|
45
46
|
* for a scope-down statement alongside it to say which requests those are. A
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { simAwsProxiedSourceIp } from "../../../serve/http/sim-aws-proxied-connection.js";
|
|
2
1
|
import { invalidSimWafRule } from "./sim-waf-rule-refusals.js";
|
|
3
2
|
import { refuseUnsimulatedSimWafRateKeyType } from "./sim-waf-unsimulated-rate-based.js";
|
|
4
3
|
/**
|
|
@@ -66,10 +65,11 @@ export function simWafRateWindowMilliseconds(statement, ruleName) {
|
|
|
66
65
|
/**
|
|
67
66
|
* Read how a rate-based statement groups the requests it counts.
|
|
68
67
|
*
|
|
69
|
-
* `IP` counts each client address on its own.
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
68
|
+
* `IP` counts each client address on its own. A request comes from
|
|
69
|
+
* `127.0.0.1` unless it states another address with `x-sim-aws-source-ip`, so
|
|
70
|
+
* a test that states none has one client for the whole simulation. That is
|
|
71
|
+
* the case a rate limiting test is usually written about: one client, sending
|
|
72
|
+
* until the rule trips.
|
|
73
73
|
*
|
|
74
74
|
* `CONSTANT` counts every request the statement sees together, and AWS asks
|
|
75
75
|
* for a scope-down statement alongside it to say which requests those are. A
|
|
@@ -79,7 +79,7 @@ export function simWafRateAggregation(statement, ruleName) {
|
|
|
79
79
|
const keyType = statement.AggregateKeyType;
|
|
80
80
|
refuseUnsimulatedSimWafRateKeyType(keyType, ruleName);
|
|
81
81
|
if (keyType === "IP") {
|
|
82
|
-
return () =>
|
|
82
|
+
return (request) => request.sourceIp;
|
|
83
83
|
}
|
|
84
84
|
if (keyType === "CONSTANT") {
|
|
85
85
|
if (statement.ScopeDownStatement === undefined) {
|
|
@@ -18,6 +18,7 @@ export function simWafBlockedResponse(customResponse, bodies) {
|
|
|
18
18
|
contentType: body?.contentType ?? fallback.contentType,
|
|
19
19
|
body: body?.content ?? fallback.body,
|
|
20
20
|
headers: simWafResponseHeaders(customResponse.ResponseHeaders),
|
|
21
|
+
isCustom: true,
|
|
21
22
|
};
|
|
22
23
|
}
|
|
23
24
|
/**
|
|
@@ -260,6 +260,11 @@ Origin again. A rule carrying it alone, with no `ResponsePagePath`, configures e
|
|
|
260
260
|
status the Distribution serves no page for. See
|
|
261
261
|
[What a Distribution stores](#what-a-distribution-stores).
|
|
262
262
|
|
|
263
|
+
A request the Distribution's [web ACL](#web-acls) blocks gets the custom error response for 403
|
|
264
|
+
as well. CloudFront cannot tell WAF's 403 from the Origin's, so a site mapping 403 to its not-found
|
|
265
|
+
page serves that page to a blocked request too. A block action with a `CustomResponse` of its own
|
|
266
|
+
is the exception (see [Web ACLs](#web-acls)).
|
|
267
|
+
|
|
263
268
|
## Serve simulated CloudFront on localhost
|
|
264
269
|
|
|
265
270
|
Use `serveSimAws` to send HTTP requests through the simulated distribution on localhost.
|
|
@@ -1846,7 +1851,7 @@ the site serves from the Origin (see [Limitations](#limitations)).
|
|
|
1846
1851
|
|
|
1847
1852
|
A Distribution can put a WAFv2 web ACL in front of everything it serves. Name the web ACL's ARN in
|
|
1848
1853
|
`WebACLId` and the Distribution evaluates it against every request that arrives. A request the web
|
|
1849
|
-
ACL blocks
|
|
1854
|
+
ACL blocks is answered at the edge. A request it allows carries on to the cache Behavior and the
|
|
1850
1855
|
Origin.
|
|
1851
1856
|
|
|
1852
1857
|
CloudFront takes its web ACL this way. WAFv2's `AssociateWebACL` covers the regional resource types.
|
|
@@ -1859,6 +1864,17 @@ simulation never created, is refused with `InvalidWebACLId` at `CreateDistributi
|
|
|
1859
1864
|
The web ACL decides before any other stage sees the request. A blocked request never reaches a
|
|
1860
1865
|
viewer-request CloudFront Function, a cache Behavior, a response headers policy or the Origin.
|
|
1861
1866
|
|
|
1867
|
+
What a blocked request gets depends on the block action. A `CustomResponse` on the action is sent
|
|
1868
|
+
as the rule describes it, whatever its status, and the Distribution's `CustomErrorResponses` have no
|
|
1869
|
+
say. With no `CustomResponse`, WAF answers 403 and the Distribution treats it as it treats a 403
|
|
1870
|
+
from the Origin. A custom error response for 403 with a `ResponsePagePath` replaces it with the
|
|
1871
|
+
response page and its `ResponseCode`. Without one, the viewer gets WAF's 403. The order is the one
|
|
1872
|
+
[AWS documents for blocked requests](https://docs.aws.amazon.com/waf/latest/developerguide/customizing-the-response-for-blocked-requests.html).
|
|
1873
|
+
|
|
1874
|
+
A blocked request's answer is never stored in the Distribution's cache, so `ErrorCachingMinTTL`
|
|
1875
|
+
has no effect on it. The next viewer to ask for the same URL is answered by WAF's decision about
|
|
1876
|
+
their own request.
|
|
1877
|
+
|
|
1862
1878
|
A CloudFormation Distribution naming a web ACL this simulation does not hold deploys without one.
|
|
1863
1879
|
The `WebACLId` lands on `stack.ignoredProperties` and every request is served, including the ones
|
|
1864
1880
|
the web ACL would have decided. A template naming a web ACL from a real account is ordinary, and a
|
|
@@ -771,6 +771,12 @@ was created, exactly as `runAs` behaves. The header is stripped before the reque
|
|
|
771
771
|
simulated service. A Lambda handler echoing `event.headers` sees none of the simulator's control
|
|
772
772
|
metadata.
|
|
773
773
|
|
|
774
|
+
`x-sim-aws-source-ip` is a control header of the same kind. It names the client address a request
|
|
775
|
+
comes from (one IPv4 or IPv6 address), and a request naming none comes from `127.0.0.1`. A web ACL
|
|
776
|
+
reads it (see
|
|
777
|
+
[where a request comes from](https://yulinsim.dev/services/wafv2/#where-a-request-comes-from)). IAM
|
|
778
|
+
policy evaluation does not read it.
|
|
779
|
+
|
|
774
780
|
```typescript sim-iam-served-request-caller
|
|
775
781
|
/**
|
|
776
782
|
* Naming the caller of an HTTP request into simulated AWS.
|