@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.
Files changed (52) 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/cloudfront/controller/dependency/sim-cf-controller-dependency.js +6 -3
  8. package/dist/service/cloudfront/controller/sim-cloudfront-request-pipeline.js +2 -1
  9. package/dist/service/cloudfront/controller/web-acl/sim-cf-web-acl-guard.d.ts +25 -3
  10. package/dist/service/cloudfront/controller/web-acl/sim-cf-web-acl-guard.js +35 -6
  11. package/dist/service/cognito/serve/sim-cognito-controller.js +4 -3
  12. package/dist/service/cognito/serve/sim-cognito-domain-controller.js +2 -0
  13. package/dist/service/cognito/serve/sim-cognito-web-acl-inspection.d.ts +2 -0
  14. package/dist/service/cognito/serve/sim-cognito-web-acl-inspection.js +1 -0
  15. package/dist/service/iam/index.d.ts +1 -0
  16. package/dist/service/iam/index.js +1 -0
  17. package/dist/service/iam/request/error/sim-aws-request-auth.error.d.ts +1 -1
  18. package/dist/service/iam/request/error/sim-aws-request-auth.error.js +1 -1
  19. package/dist/service/iam/request/sim-aws-control-headers.d.ts +2 -1
  20. package/dist/service/iam/request/sim-aws-control-headers.js +4 -1
  21. package/dist/service/iam/request/sim-aws-source-ip-header.d.ts +19 -0
  22. package/dist/service/iam/request/sim-aws-source-ip-header.js +32 -0
  23. package/dist/service/wafv2/association/sim-waf-associations.js +1 -1
  24. package/dist/service/wafv2/association/sim-waf-protection.d.ts +5 -0
  25. package/dist/service/wafv2/association/sim-waf-request-inspection.d.ts +2 -0
  26. package/dist/service/wafv2/association/sim-waf-request-inspection.js +7 -2
  27. package/dist/service/wafv2/evaluate/sim-waf-blocked-response.d.ts +12 -0
  28. package/dist/service/wafv2/evaluate/sim-waf-blocked-response.js +1 -0
  29. package/dist/service/wafv2/evaluate/sim-waf-evaluation-request.d.ts +5 -0
  30. package/dist/service/wafv2/evaluate/sim-waf-inspected-request.d.ts +12 -1
  31. package/dist/service/wafv2/evaluate/sim-waf-inspected-request.js +12 -1
  32. package/dist/service/wafv2/index.d.ts +2 -1
  33. package/dist/service/wafv2/index.js +1 -0
  34. package/dist/service/wafv2/managed/group/sim-waf-ip-reputation.d.ts +8 -5
  35. package/dist/service/wafv2/managed/group/sim-waf-ip-reputation.js +10 -5
  36. package/dist/service/wafv2/managed/sim-waf-managed-address-declaration.d.ts +32 -0
  37. package/dist/service/wafv2/managed/sim-waf-managed-address-declaration.js +52 -0
  38. package/dist/service/wafv2/managed/sim-waf-managed-address-range.d.ts +21 -0
  39. package/dist/service/wafv2/managed/sim-waf-managed-address-range.js +73 -0
  40. package/dist/service/wafv2/managed/sim-waf-managed-rule-groups.d.ts +7 -0
  41. package/dist/service/wafv2/managed/sim-waf-managed-rule-groups.js +15 -0
  42. package/dist/service/wafv2/managed/sim-waf-managed-rule.type.d.ts +5 -0
  43. package/dist/service/wafv2/managed/sim-waf-managed-rules.d.ts +27 -1
  44. package/dist/service/wafv2/managed/sim-waf-managed-rules.js +33 -13
  45. package/dist/service/wafv2/sim-wafv2.js +2 -1
  46. package/dist/service/wafv2/statement/sim-waf-rate-based-input.d.ts +5 -4
  47. package/dist/service/wafv2/statement/sim-waf-rate-based-input.js +6 -6
  48. package/dist/service/wafv2/web-acl/sim-waf-action-input.js +1 -0
  49. package/docs/services/cloudfront/README.md +17 -1
  50. package/docs/services/iam/README.md +6 -0
  51. package/docs/services/wafv2/README.md +112 -15
  52. package/package.json +3 -3
@@ -282,10 +282,11 @@ lets evaluation continue, the way it does for every other statement kind.
282
282
  window is measured against [simulated time](https://yulinsim.dev/time/), so `advanceBy` past it drops
283
283
  what the rule counted.
284
284
 
285
- `AggregateKeyType` is `IP` or `CONSTANT`. `IP` counts each client address on its own. Every request
286
- in this simulation reports `127.0.0.1`, leaving a web ACL with one client for the whole simulation.
287
- That is the case a rate limiting test is written about anyway (one client, sending until the rule
288
- trips), and it behaves here as it does on AWS. `CONSTANT` counts every request the statement sees
285
+ `AggregateKeyType` is `IP` or `CONSTANT`. `IP` counts each client address on its own. A request
286
+ comes from `127.0.0.1` unless it names another address (see
287
+ [Where a request comes from](#where-a-request-comes-from)). A test that names none has one client for
288
+ the whole simulation. That is the case a rate limiting test is usually written about (one client,
289
+ sending until the rule trips). `CONSTANT` counts every request the statement sees
289
290
  together, and AWS requires a `ScopeDownStatement` alongside it to say which requests those are.
290
291
 
291
292
  A `ScopeDownStatement` narrows what the rule counts. Every statement kind in
@@ -463,17 +464,103 @@ are.
463
464
 
464
465
  ### Groups that read the caller
465
466
 
466
- The IP reputation, anonymous IP and Bot Control groups decide by who sent a request, and every
467
- request in this simulation comes from `127.0.0.1`. Each of their rules is declared-only. The group
468
- labels the request, takes the rule's documented action and honours overrides, as the groups that
469
- read the request do.
467
+ The IP reputation, anonymous IP and Bot Control groups decide by who sent a request. AWS publishes
468
+ their rule names, actions and labels, and none of the address lists or bot signatures behind them.
469
+ Each of their rules is declared-only. The group labels the request, takes the rule's documented
470
+ action and honours overrides, as the groups that read the request do.
470
471
 
471
472
  `AWSManagedRulesAmazonIpReputationList` holds `AWSManagedIPReputationList` and
472
473
  `AWSManagedReconnaissanceList`, which block, and `AWSManagedIPDDoSList`, which counts as AWS ships
473
474
  it. `AWSManagedRulesAnonymousIpList` holds `AnonymousIPList` (VPNs, proxies and Tor) and
474
475
  `HostingProviderIPList` (hosting and cloud providers other than AWS), which both block. A test
475
- declares a match by rule name, as above, and says which requests come from a flagged address that
476
- way.
476
+ declares which addresses those five rules flag, and sends requests from them.
477
+
478
+ #### Where a request comes from
479
+
480
+ A request reaches this simulation in process or over loopback, and comes from `127.0.0.1`. The
481
+ `x-sim-aws-source-ip` header names another client address (one IPv4 or IPv6 address). It works
482
+ through `SimAwsHttp` and `serveSimAws` alike, and it is stripped before the request reaches the
483
+ simulated service, as `x-sim-aws-caller` is. A value that is no address is refused with a 400 and
484
+ `InvalidRequestCaller`. `evaluateRequest` takes the address as `sourceIp`.
485
+
486
+ The address reaches the web ACL in front of a Cognito user pool or a REST API stage. The IP
487
+ reputation and anonymous IP rules read it, and so does a rate limit aggregating on `IP`.
488
+
489
+ `managedRules().onAddress` says which of those rules flag one address or a CIDR range. Only the
490
+ rules of the IP reputation and anonymous IP groups can be declared by address, and a rule of any
491
+ other group is refused. Declaring the same range again replaces it. An address inside two declared
492
+ ranges is flagged by the rules of both, and a match declared for the request's path with
493
+ `onRequest` applies beside them.
494
+
495
+ ```typescript sim-wafv2-anonymous-ip-list
496
+ /**
497
+ * Blocking sign-ups from a Tor exit range with the anonymous IP list.
498
+ */
499
+
500
+ import { CreateWebACLCommand } from "@aws-sdk/client-wafv2";
501
+
502
+ import { SimAws } from "@kensio/yulin";
503
+
504
+ const waf = new SimAws().wafV2();
505
+
506
+ const visibility = {
507
+ SampledRequestsEnabled: false,
508
+ CloudWatchMetricsEnabled: false,
509
+ MetricName: "pool",
510
+ };
511
+
512
+ const created = await waf.createWebAcl(
513
+ new CreateWebACLCommand({
514
+ Name: "pool-acl",
515
+ Scope: "REGIONAL",
516
+ DefaultAction: { Allow: {} },
517
+ VisibilityConfig: visibility,
518
+ Rules: [
519
+ {
520
+ Name: "anonymous-ip",
521
+ Priority: 0,
522
+ OverrideAction: { None: {} },
523
+ Statement: {
524
+ ManagedRuleGroupStatement: {
525
+ VendorName: "AWS",
526
+ Name: "AWSManagedRulesAnonymousIpList",
527
+ },
528
+ },
529
+ VisibilityConfig: { ...visibility, MetricName: "anonymous-ip" },
530
+ },
531
+ ],
532
+ }),
533
+ );
534
+
535
+ waf.managedRules().onAddress("185.220.101.0/24", {
536
+ matches: ["AnonymousIPList"],
537
+ });
538
+
539
+ const signUp = (sourceIp: string): string =>
540
+ waf.evaluateRequest({
541
+ webAclArn: created.Summary!.ARN,
542
+ request: new Request("https://myapp-login.example.com/signup", {
543
+ headers: { "user-agent": "Mozilla/5.0" },
544
+ }),
545
+ sourceIp,
546
+ }).action;
547
+
548
+ // "BLOCK"
549
+ console.log(signUp("185.220.101.7"));
550
+
551
+ // "ALLOW"
552
+ console.log(signUp("203.0.113.9"));
553
+ ```
554
+
555
+ A request over HTTP names its address in the header instead:
556
+
557
+ ```text
558
+ POST /signup HTTP/1.1
559
+ Host: myapp-login.auth.eu-west-2.amazoncognito.com
560
+ x-sim-aws-source-ip: 185.220.101.7
561
+ ```
562
+
563
+ The other kinds of statement that read the address are still held (see [Held input](#held-input)).
477
564
 
478
565
  Bot Control labels a request by the bot that sent it, and a verified bot is labelled and matches no
479
566
  rule. A test declares the bot.
@@ -865,6 +952,9 @@ and records no message.
865
952
 
866
953
  The pages are usually the point. `/signup`, `/confirm` and `/forgotPassword` are the ones that
867
954
  create an account or send an email, and a real web ACL on a user pool is usually written for them.
955
+ A request to them can name the client it comes from in the `x-sim-aws-source-ip` header, for the
956
+ anonymous IP list and the IP reputation list to decide on (see
957
+ [Where a request comes from](#where-a-request-comes-from)).
868
958
 
869
959
  ```typescript sim-wafv2-cognito-user-pool
870
960
  /**
@@ -1006,9 +1096,15 @@ its assets are outside the simulation.
1006
1096
  ## Protecting a CloudFront distribution
1007
1097
 
1008
1098
  A simulated CloudFront distribution names its web ACL in `WebACLId` on its `DistributionConfig`,
1009
- and evaluates it against every request that reaches the distribution. A blocked request gets 403
1099
+ and evaluates it against every request that reaches the distribution. A blocked request is answered
1010
1100
  before a cache behaviour, a viewer-request CloudFront Function or the origin sees it.
1011
1101
 
1102
+ WAF's default 403 goes through the distribution's custom error responses, as a 403 from the origin
1103
+ would. A distribution mapping 403 to a not-found page serves that page, with its `ResponseCode`, to
1104
+ a request the web ACL blocked. A block action with a `CustomResponse` takes precedence. Its status
1105
+ and body reach the viewer unchanged, even when the distribution has a custom error response for the
1106
+ same status.
1107
+
1012
1108
  CloudFront is associated this way and not through `AssociateWebACL`, which real WAF keeps for the
1013
1109
  regional resource types. The ARN has to name a `CLOUDFRONT` [scope](#scopes) web ACL. See
1014
1110
  [web ACLs in the CloudFront docs](https://yulinsim.dev/services/cloudfront/#web-acls) for the whole example.
@@ -1537,9 +1633,10 @@ where AWS blocks every address outside the list and the simulation blocks none.
1537
1633
 
1538
1634
  A rule is held for any of these statement kinds:
1539
1635
 
1540
- - `IPSetReferenceStatement`, `GeoMatchStatement` and `AsnMatchStatement`. Every request in this
1541
- simulation reports a source address of `127.0.0.1`, and a rule on where a request came from would
1542
- see one client for the whole simulation.
1636
+ - `IPSetReferenceStatement`, `GeoMatchStatement` and `AsnMatchStatement`. A request has a client
1637
+ address (see [Where a request comes from](#where-a-request-comes-from)), and these statements are
1638
+ not yet written to read it. A country or an ASN would also need an address database the simulation
1639
+ does not have.
1543
1640
  - `SqliMatchStatement` and `XssMatchStatement`. AWS publishes no description of the detection they
1544
1641
  run.
1545
1642
  - `RuleGroupReferenceStatement`. A rule group of your own is a resource in its own right, and none
@@ -1551,7 +1648,7 @@ A rule is held for any of these statement kinds:
1551
1648
 
1552
1649
  A `RateBasedStatement` is evaluated (see [Rate limiting](#rate-limiting)). A rule using two of its
1553
1650
  aggregation key types is held. `FORWARDED_IP` and `ForwardedIPConfig` read the address from a
1554
- forwarding header, which needs the source address variety an IP set is waiting on. `CUSTOM_KEYS` and
1651
+ forwarding header, and are held along with the IP set statement. `CUSTOM_KEYS` and
1555
1652
  `CustomKeys` aggregate on headers, cookies and query arguments, and are feasible and not part of
1556
1653
  this yet. `GetRateBasedStatementManagedKeys` is not simulated.
1557
1654
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kensio/yulin",
3
- "version": "1.22.0",
3
+ "version": "1.23.1",
4
4
  "description": "AWS system behaviour simulation for isolated unit testing",
5
5
  "repository": "https://github.com/KensioSoftware/yulin",
6
6
  "homepage": "https://yulinsim.dev/",
@@ -298,9 +298,9 @@
298
298
  "execa": "^10.0.0",
299
299
  "fta-cli": "^3.0.0",
300
300
  "hyparquet": "^1.29.2",
301
- "hyparquet-writer": "^0.17.0",
301
+ "hyparquet-writer": "^0.18.0",
302
302
  "node-sql-parser": "^5.4.0",
303
- "oxfmt": "^0.74.0",
303
+ "oxfmt": "^0.75.0",
304
304
  "oxlint": "^1.77.0",
305
305
  "oxlint-tsgolint": "^7.0.2001",
306
306
  "semantic-release": "25.0.9",