@kensio/yulin 1.21.34 → 1.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/serve/controller/sim-service-controller.d.ts +6 -0
  2. package/dist/serve/controller/sim-service-controller.js +7 -0
  3. package/dist/serve/http/request/sim-aws-request-authenticator.js +3 -0
  4. package/dist/service/apigateway/serve/sim-api-gateway-controller.js +1 -0
  5. package/dist/service/apigateway/serve/sim-rest-api-web-acl-inspection.d.ts +2 -0
  6. package/dist/service/apigateway/serve/sim-rest-api-web-acl-inspection.js +1 -0
  7. package/dist/service/cloudformation/registry/sim-cfn-attribute-reads.d.ts +11 -0
  8. package/dist/service/cloudformation/registry/sim-cfn-attribute-reads.js +64 -0
  9. package/dist/service/cloudformation/registry/sim-cfn-read-handler-authorizer.d.ts +45 -0
  10. package/dist/service/cloudformation/registry/sim-cfn-read-handler-authorizer.js +72 -0
  11. package/dist/service/cloudformation/registry/sim-cfn-read-handler.d.ts +33 -0
  12. package/dist/service/cloudformation/registry/sim-cfn-read-handler.fixture.d.ts +24 -0
  13. package/dist/service/cloudformation/registry/sim-cfn-read-handler.fixture.js +58 -0
  14. package/dist/service/cloudformation/registry/sim-cfn-read-handler.js +36 -0
  15. package/dist/service/cloudformation/registry/sim-cfn-read-handlers.d.ts +10 -0
  16. package/dist/service/cloudformation/registry/sim-cfn-read-handlers.js +23 -0
  17. package/dist/service/cloudformation/registry/sim-cfn-read-handlers.json +7400 -0
  18. package/dist/service/cloudformation/registry/sim-cfn-resource-arn.d.ts +46 -0
  19. package/dist/service/cloudformation/registry/sim-cfn-resource-arn.js +119 -0
  20. package/dist/service/cloudformation/stack/sim-cfn-stack-deploy-context.d.ts +6 -1
  21. package/dist/service/cloudformation/stack/sim-cfn-stack-deploy-context.js +3 -0
  22. package/dist/service/cloudformation/stack/sim-cfn-stack-resource-operations.d.ts +3 -2
  23. package/dist/service/cloudformation/stack/sim-cfn-stack-resource-operations.js +2 -1
  24. package/dist/service/cloudformation/stack/sim-cfn-stack.js +9 -0
  25. package/dist/service/cloudformation/stack/update/sim-cfn-stack-update-plan.d.ts +11 -0
  26. package/dist/service/cloudformation/stack/update/sim-cfn-stack-update-plan.js +22 -0
  27. package/dist/service/cloudformation/stack/update/sim-cfn-stack-updater.d.ts +3 -1
  28. package/dist/service/cloudformation/stack/update/sim-cfn-stack-updater.js +2 -1
  29. package/dist/service/cognito/serve/sim-cognito-controller.js +4 -3
  30. package/dist/service/cognito/serve/sim-cognito-domain-controller.js +2 -0
  31. package/dist/service/cognito/serve/sim-cognito-web-acl-inspection.d.ts +2 -0
  32. package/dist/service/cognito/serve/sim-cognito-web-acl-inspection.js +1 -0
  33. package/dist/service/iam/index.d.ts +1 -0
  34. package/dist/service/iam/index.js +1 -0
  35. package/dist/service/iam/request/error/sim-aws-request-auth.error.d.ts +1 -1
  36. package/dist/service/iam/request/error/sim-aws-request-auth.error.js +1 -1
  37. package/dist/service/iam/request/sim-aws-control-headers.d.ts +2 -1
  38. package/dist/service/iam/request/sim-aws-control-headers.js +4 -1
  39. package/dist/service/iam/request/sim-aws-source-ip-header.d.ts +19 -0
  40. package/dist/service/iam/request/sim-aws-source-ip-header.js +32 -0
  41. package/dist/service/stepfunctions/command/execution/sim-states-execution-start.js +1 -0
  42. package/dist/service/stepfunctions/execution/sim-states-run-task.js +1 -0
  43. package/dist/service/stepfunctions/execution/sim-states-state-outcome.d.ts +1 -0
  44. package/dist/service/stepfunctions/execution/sim-states-walks.d.ts +1 -0
  45. package/dist/service/stepfunctions/execution/sim-states-walks.js +1 -0
  46. package/dist/service/stepfunctions/task/sim-aws-states-task-targets.js +6 -1
  47. package/dist/service/stepfunctions/task/sim-states-execution-role.d.ts +2 -1
  48. package/dist/service/stepfunctions/task/sim-states-execution-role.js +2 -1
  49. package/dist/service/stepfunctions/task/sim-states-task-invocation.d.ts +1 -0
  50. package/dist/service/wafv2/association/sim-waf-associations.js +1 -1
  51. package/dist/service/wafv2/association/sim-waf-protection.d.ts +5 -0
  52. package/dist/service/wafv2/association/sim-waf-request-inspection.d.ts +2 -0
  53. package/dist/service/wafv2/association/sim-waf-request-inspection.js +7 -2
  54. package/dist/service/wafv2/evaluate/sim-waf-evaluation-request.d.ts +5 -0
  55. package/dist/service/wafv2/evaluate/sim-waf-inspected-request.d.ts +12 -1
  56. package/dist/service/wafv2/evaluate/sim-waf-inspected-request.js +12 -1
  57. package/dist/service/wafv2/index.d.ts +2 -1
  58. package/dist/service/wafv2/index.js +1 -0
  59. package/dist/service/wafv2/managed/group/sim-waf-ip-reputation.d.ts +8 -5
  60. package/dist/service/wafv2/managed/group/sim-waf-ip-reputation.js +10 -5
  61. package/dist/service/wafv2/managed/sim-waf-managed-address-declaration.d.ts +32 -0
  62. package/dist/service/wafv2/managed/sim-waf-managed-address-declaration.js +52 -0
  63. package/dist/service/wafv2/managed/sim-waf-managed-address-range.d.ts +21 -0
  64. package/dist/service/wafv2/managed/sim-waf-managed-address-range.js +73 -0
  65. package/dist/service/wafv2/managed/sim-waf-managed-rule-groups.d.ts +7 -0
  66. package/dist/service/wafv2/managed/sim-waf-managed-rule-groups.js +15 -0
  67. package/dist/service/wafv2/managed/sim-waf-managed-rule.type.d.ts +5 -0
  68. package/dist/service/wafv2/managed/sim-waf-managed-rules.d.ts +27 -1
  69. package/dist/service/wafv2/managed/sim-waf-managed-rules.js +33 -13
  70. package/dist/service/wafv2/sim-wafv2.js +2 -1
  71. package/dist/service/wafv2/statement/sim-waf-rate-based-input.d.ts +5 -4
  72. package/dist/service/wafv2/statement/sim-waf-rate-based-input.js +6 -6
  73. package/docs/services/cloudformation/README.md +106 -0
  74. package/docs/services/iam/README.md +6 -0
  75. package/docs/services/wafv2/README.md +105 -14
  76. package/package.json +5 -4
@@ -46,12 +46,13 @@ const refusals = {
46
46
  * rule, which reaches a function as a service principal and is admitted by the
47
47
  * function's resource policy.
48
48
  */
49
- export async function assumeSimStatesExecutionRole(target, scope) {
49
+ export async function assumeSimStatesExecutionRole(target, scope, source) {
50
50
  return await assumeSimServiceRole({
51
51
  target,
52
52
  servicePrincipal: simStatesServicePrincipal,
53
53
  sessionName,
54
54
  scope,
55
55
  refusals,
56
+ source,
56
57
  });
57
58
  }
@@ -15,6 +15,7 @@ export interface SimStatesTaskInvocation {
15
15
  * work.
16
16
  */
17
17
  readonly roleArn: string;
18
+ readonly stateMachineArn: string;
18
19
  }
19
20
  /**
20
21
  * Where a `Task` state's work is done.
@@ -75,7 +75,7 @@ export class SimWafAssociations {
75
75
  if (association === undefined) {
76
76
  return undefined;
77
77
  }
78
- return association.webAcl.evaluate(simWafInspectedRequest(request.request, request.body, association.webAcl.bodyInspectionLimitBytes(association.resourceType)));
78
+ return association.webAcl.evaluate(simWafInspectedRequest(request.request, request.body, association.webAcl.bodyInspectionLimitBytes(association.resourceType), request.sourceIp));
79
79
  }
80
80
  /**
81
81
  * Forget whatever is in front of one resource.
@@ -6,6 +6,11 @@ export interface SimWafProtectedRequest {
6
6
  /** The resource the request reached, by ARN. */
7
7
  readonly resourceArn: string;
8
8
  readonly request: Request;
9
+ /**
10
+ * The client address the request came from, which is loopback when nothing
11
+ * says otherwise.
12
+ */
13
+ readonly sourceIp?: string | undefined;
9
14
  /**
10
15
  * The request body, when the rules might inspect it. A request body is a
11
16
  * stream that cannot be read twice, so it is passed in already buffered.
@@ -5,6 +5,8 @@ interface SimWafInspectionInput {
5
5
  /** The resource the request reached, by ARN. */
6
6
  readonly resourceArn: string;
7
7
  readonly request: Request;
8
+ /** The client address the request came from. */
9
+ readonly sourceIp: string;
8
10
  /**
9
11
  * Whether the fronting service forwards the request body to AWS WAF.
10
12
  *
@@ -27,12 +27,17 @@ export class SimWafRequestInspection {
27
27
  * Put one request to whatever protects the resource it addressed.
28
28
  */
29
29
  async inspect(input) {
30
- const { protection, resourceArn, request } = input;
30
+ const { protection, resourceArn, request, sourceIp } = input;
31
31
  if (!protection.protects(resourceArn)) {
32
32
  return new SimWafInspected({ request });
33
33
  }
34
34
  const body = input.forwardBody ? await inspectedBody(request) : undefined;
35
- const decision = protection.decide({ resourceArn, request, body });
35
+ const decision = protection.decide({
36
+ resourceArn,
37
+ request,
38
+ sourceIp,
39
+ body,
40
+ });
36
41
  // A decision carries a blocked response when its action was to block, and
37
42
  // carries none when the request was allowed. That one reading is what
38
43
  // decides the request here.
@@ -6,6 +6,11 @@ export interface SimWafEvaluationRequest {
6
6
  /** The web ACL to evaluate against, by ARN. */
7
7
  readonly webAclArn: string;
8
8
  readonly request: Request;
9
+ /**
10
+ * The client address the request came from. A request naming none comes
11
+ * from `127.0.0.1`, as every request does that states no other address.
12
+ */
13
+ readonly sourceIp?: string | undefined;
9
14
  /**
10
15
  * The request body, when the rules might inspect it. A request body is a
11
16
  * stream that cannot be read twice, so it is passed in already buffered.
@@ -20,6 +20,14 @@ export interface SimWafInspectedRequest {
20
20
  * header with the Yulin-local suffix taken off it.
21
21
  */
22
22
  readonly host: string;
23
+ /**
24
+ * The client address the request came from, which is what the IP-based
25
+ * rules read.
26
+ *
27
+ * Nothing reaches this simulation over a public network, so this is
28
+ * `127.0.0.1` unless the request said otherwise with `x-sim-aws-source-ip`.
29
+ */
30
+ readonly sourceIp: string;
23
31
  /** The request body, or nothing when the request carried none. */
24
32
  readonly body: Uint8Array | undefined;
25
33
  /**
@@ -49,5 +57,8 @@ export interface SimWafInspectedRequest {
49
57
  * The body inspection limit defaults to the 16 KB every resource type this
50
58
  * simulation protects reads by default. A web ACL raising it with
51
59
  * `AssociationConfig` passes the raised figure in.
60
+ *
61
+ * The client address defaults to loopback, which is where a request comes from
62
+ * when it states no other address.
52
63
  */
53
- export declare function simWafInspectedRequest(request: Request, body?: Uint8Array, bodyInspectionLimitBytes?: number): SimWafInspectedRequest;
64
+ export declare function simWafInspectedRequest(request: Request, body?: Uint8Array, bodyInspectionLimitBytes?: number, sourceIp?: string): SimWafInspectedRequest;
@@ -1,4 +1,7 @@
1
+ import { isIP } from "node:net";
2
+ import { simAwsProxiedSourceIp } from "../../../serve/http/sim-aws-proxied-connection.js";
1
3
  import { simAwsRequestHostname } from "../../../serve/http/url/sim-aws-request-hostname.js";
4
+ import { SimWafDeclarationError } from "../error/sim-wafv2.error.js";
2
5
  import { simWafBodyInspectionLimitBytes } from "../web-acl/sim-waf-association-config.js";
3
6
  import { SimWafRequestLabels } from "./sim-waf-request-labels.js";
4
7
  /**
@@ -16,8 +19,15 @@ import { SimWafRequestLabels } from "./sim-waf-request-labels.js";
16
19
  * The body inspection limit defaults to the 16 KB every resource type this
17
20
  * simulation protects reads by default. A web ACL raising it with
18
21
  * `AssociationConfig` passes the raised figure in.
22
+ *
23
+ * The client address defaults to loopback, which is where a request comes from
24
+ * when it states no other address.
19
25
  */
20
- export function simWafInspectedRequest(request, body, bodyInspectionLimitBytes = simWafBodyInspectionLimitBytes) {
26
+ export function simWafInspectedRequest(request, body, bodyInspectionLimitBytes = simWafBodyInspectionLimitBytes, sourceIp = simAwsProxiedSourceIp) {
27
+ if (isIP(sourceIp) === 0) {
28
+ throw new SimWafDeclarationError(`A request's source address must be one IPv4 or IPv6 address, and ` +
29
+ `${sourceIp} is neither`);
30
+ }
21
31
  const url = new URL(request.url);
22
32
  return {
23
33
  method: request.method,
@@ -28,6 +38,7 @@ export function simWafInspectedRequest(request, body, bodyInspectionLimitBytes =
28
38
  queryString: url.search.replace(/^\?/u, ""),
29
39
  headers: request.headers,
30
40
  host: simAwsRequestHostname(request),
41
+ sourceIp,
31
42
  body,
32
43
  bodyInspectionLimitBytes,
33
44
  labels: new SimWafRequestLabels(),
@@ -25,13 +25,14 @@ export { SimWafIpSet, type SimWafIpAddressVersion, } from "./ip-set/sim-waf-ip-s
25
25
  export { SimWafRegexPatternSet } from "./regex-pattern-set/sim-waf-regex-pattern-set.js";
26
26
  export type { SimWafDecision } from "./evaluate/sim-waf-decision.js";
27
27
  export { SimWafRequestLabels } from "./evaluate/sim-waf-request-labels.js";
28
- export { SimWafManagedRules, type SimWafManagedMatchDeclaration, } from "./managed/sim-waf-managed-rules.js";
28
+ export { SimWafManagedRules, type SimWafManagedAddressDeclaration, type SimWafManagedMatchDeclaration, } from "./managed/sim-waf-managed-rules.js";
29
29
  export type { SimWafBotDeclaration } from "./managed/sim-waf-bot-declaration.js";
30
30
  export type { SimWafManagedRuleReport } from "./managed/sim-waf-managed-rule-report.js";
31
31
  export type { SimWafManagedRuleTier } from "./managed/sim-waf-managed-rule.type.js";
32
32
  export type { SimWafManagedRuleGroupStatementInput, SimWafOverrideActionInput, SimWafRuleActionOverrideInput, } from "./managed/sim-waf-managed-group.type.js";
33
33
  export { simWafBlockedHttpResponse, type SimWafBlockedResponse, simWafDefaultBlockedResponse, } from "./evaluate/sim-waf-blocked-response.js";
34
34
  export { type SimWafInspectedRequest, simWafInspectedRequest, } from "./evaluate/sim-waf-inspected-request.js";
35
+ export { simAwsSourceIpHeaderName } from "../iam/request/sim-aws-source-ip-header.js";
35
36
  export { simWafHeaderInspectionLimitBytes } from "./statement/sim-waf-field-content.js";
36
37
  export { type SimWafAssociationConfigInput, type SimWafBodyInspectionResourceType, simWafBodyInspectionLimitBytes, } from "./web-acl/sim-waf-association-config.js";
37
38
  export type { SimWafRateBasedStatementInput } from "./statement/sim-waf-rate-based.type.js";
@@ -20,6 +20,7 @@ export { SimWafRequestLabels } from "./evaluate/sim-waf-request-labels.js";
20
20
  export { SimWafManagedRules, } from "./managed/sim-waf-managed-rules.js";
21
21
  export { simWafBlockedHttpResponse, simWafDefaultBlockedResponse, } from "./evaluate/sim-waf-blocked-response.js";
22
22
  export { simWafInspectedRequest, } from "./evaluate/sim-waf-inspected-request.js";
23
+ export { simAwsSourceIpHeaderName } from "../iam/request/sim-aws-source-ip-header.js";
23
24
  export { simWafHeaderInspectionLimitBytes } from "./statement/sim-waf-field-content.js";
24
25
  export { simWafBodyInspectionLimitBytes, } from "./web-acl/sim-waf-association-config.js";
25
26
  export { SimWafAssociatedItemException, SimWafDeclarationError, SimWafDuplicateItemException, SimWafError, SimWafInvalidParameterException, SimWafNonexistentItemException, SimWafOptimisticLockException, SimWafUnavailableEntityException, SimWafUnsimulatedInputException, } from "./error/sim-wafv2.error.js";
@@ -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
- * Every request in this simulation reports a source address of `127.0.0.1`,
7
- * so every rule here is declared-only. A test says which requests come from a
8
- * flagged address with `managedRules().onRequest`, and the group labels and
9
- * acts on them as AWS documents.
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
- * Every request in this simulation reports a source address of `127.0.0.1`,
6
- * so every rule here is declared-only. A test says which requests come from a
7
- * flagged address with `managedRules().onRequest`, and the group labels and
8
- * acts on them as AWS documents.
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 { findSimWafManagedRule, } from "./sim-waf-managed-rule-groups.js";
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) => this.requiredRule(name).rule.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 this.requiredRule(ruleName).rule.tier;
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
- return webAcl.evaluate(simWafInspectedRequest(evaluation.request, evaluation.body, limitBytes));
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. Every request in this simulation
40
- * reports `simAwsProxiedSourceIp`, leaving one client for the whole
41
- * simulation. A rate limiting test sends its requests from one client and
42
- * expects the rule to trip, so that is the case anybody would write.
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