@kensio/yulin 1.20.15 → 1.20.17

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 (355) hide show
  1. package/README.md +13 -0
  2. package/dist/sdk/module/sim-sdk-module-client-interceptor.d.ts +5 -0
  3. package/dist/sdk/module/sim-sdk-module-client-interceptor.js +14 -0
  4. package/dist/sdk/module/sim-sdk-static-client-factory.d.ts +26 -0
  5. package/dist/sdk/module/sim-sdk-static-client-factory.js +59 -0
  6. package/dist/sdk/send-patch.d.ts +8 -0
  7. package/dist/sdk/send-patch.js +11 -0
  8. package/dist/service/aws/caller/sim-aws-ambient-caller.d.ts +23 -0
  9. package/dist/service/aws/caller/sim-aws-ambient-caller.js +12 -0
  10. package/dist/service/aws/caller/sim-aws-caller-resolver.d.ts +39 -6
  11. package/dist/service/aws/caller/sim-aws-caller-resolver.js +32 -30
  12. package/dist/service/aws/caller/sim-aws-caller.d.ts +9 -0
  13. package/dist/service/aws/caller/sim-aws-resolved-caller.d.ts +19 -0
  14. package/dist/service/aws/caller/sim-aws-resolved-caller.js +26 -0
  15. package/dist/service/aws/factory/sim-aws-account-region-service-builder.d.ts +12 -0
  16. package/dist/service/aws/factory/sim-aws-account-region-service-builder.js +6 -0
  17. package/dist/service/aws/factory/sim-aws-account-service-cache.d.ts +12 -0
  18. package/dist/service/aws/factory/sim-aws-account-service-cache.js +9 -0
  19. package/dist/service/aws/factory/sim-aws-registered-service-builder.js +3 -1
  20. package/dist/service/aws/factory/sim-aws-service-factory.d.ts +5 -0
  21. package/dist/service/aws/factory/sim-aws-service-factory.js +14 -0
  22. package/dist/service/aws/sim-aws-properties.d.ts +15 -0
  23. package/dist/service/aws/sim-aws.js +2 -1
  24. package/dist/service/cloudformation/command/create-stack/create-stack.handler.d.ts +4 -0
  25. package/dist/service/cloudformation/command/create-stack/create-stack.handler.js +4 -1
  26. package/dist/service/cloudformation/deploy/sim-cfn-cdk-out-deployer.js +2 -1
  27. package/dist/service/cloudformation/deploy/sim-cfn-cdk-out-plan.d.ts +11 -2
  28. package/dist/service/cloudformation/deploy/sim-cfn-cdk-out-plan.js +2 -1
  29. package/dist/service/cloudformation/deploy/sim-cfn-cdk-out-stack-options.d.ts +6 -0
  30. package/dist/service/cloudformation/deploy/sim-cfn-template-deployer.d.ts +11 -0
  31. package/dist/service/cloudformation/deploy/sim-cfn-template-deployer.js +17 -15
  32. package/dist/service/cloudformation/deploy/sim-cfn-template-file-loader.d.ts +12 -0
  33. package/dist/service/cloudformation/deploy/sim-cfn-template-file-loader.js +2 -1
  34. package/dist/service/cloudformation/resource/cfn/cloudfront/sim-cloudfront-cache-policy-cfn.d.ts +23 -0
  35. package/dist/service/cloudformation/resource/cfn/cloudfront/sim-cloudfront-cache-policy-cfn.js +30 -0
  36. package/dist/service/cloudformation/resource/cfn/cloudfront/sim-cloudfront-cfn-value-adapter.js +8 -0
  37. package/dist/service/cloudformation/resource/name/sim-cfn-generated-resource-name.d.ts +29 -12
  38. package/dist/service/cloudformation/resource/name/sim-cfn-generated-resource-name.js +51 -20
  39. package/dist/service/cloudformation/stack/deploy/sim-cfn-resource-order.d.ts +19 -0
  40. package/dist/service/cloudformation/stack/deploy/sim-cfn-resource-order.js +6 -0
  41. package/dist/service/cloudformation/stack/deploy/sim-cfn-stack-resource-batch-creator.d.ts +7 -0
  42. package/dist/service/cloudformation/stack/deploy/sim-cfn-stack-resource-batch-creator.js +9 -2
  43. package/dist/service/cloudformation/stack/deploy/sim-cfn-stack-resource-creator.d.ts +2 -0
  44. package/dist/service/cloudformation/stack/deploy/sim-cfn-stack-resource-creator.js +2 -1
  45. package/dist/service/cloudformation/stack/sim-cfn-stack-resource-operations.d.ts +6 -0
  46. package/dist/service/cloudformation/stack/sim-cfn-stack-resource-operations.js +4 -1
  47. package/dist/service/cloudformation/stack/sim-cfn-stack.js +2 -1
  48. package/dist/service/cloudformation/stack/sim-cfn-stack.type.d.ts +6 -0
  49. package/dist/service/cloudfront/behaviour/sim-cloud-front-behavior.d.ts +5 -0
  50. package/dist/service/cloudfront/cache-policy/sim-cf-cache-policy-registry.d.ts +37 -0
  51. package/dist/service/cloudfront/cache-policy/sim-cf-cache-policy-registry.js +50 -0
  52. package/dist/service/cloudfront/cache-policy/sim-cf-cache-policy.d.ts +25 -0
  53. package/dist/service/cloudfront/cache-policy/sim-cf-cache-policy.js +21 -0
  54. package/dist/service/cloudfront/cache-policy/sim-cf-managed-cache-policies.d.ts +28 -0
  55. package/dist/service/cloudfront/cache-policy/sim-cf-managed-cache-policies.js +48 -0
  56. package/dist/service/cloudfront/cff/function-code-input/cff-function-code-input.d.ts +8 -0
  57. package/dist/service/cloudfront/cff/function-code-input/cff-function-code-input.js +13 -0
  58. package/dist/service/cloudfront/cff/sim-cf-function-commands.d.ts +52 -0
  59. package/dist/service/cloudfront/cff/sim-cf-function-commands.js +51 -0
  60. package/dist/service/cloudfront/cff/sim-cff-configuration.d.ts +41 -0
  61. package/dist/service/cloudfront/cff/sim-cff-configuration.js +34 -0
  62. package/dist/service/cloudfront/cff/sim-cff-stage.d.ts +23 -0
  63. package/dist/service/cloudfront/cff/sim-cff-stage.js +30 -0
  64. package/dist/service/cloudfront/cff/sim-cloudfront-function.d.ts +6 -1
  65. package/dist/service/cloudfront/cff/sim-cloudfront-function.js +6 -0
  66. package/dist/service/cloudfront/cfn/cache-policy/sim-cfn-cf-cache-policy-config.d.ts +27 -0
  67. package/dist/service/cloudfront/cfn/cache-policy/sim-cfn-cf-cache-policy-config.js +42 -0
  68. package/dist/service/cloudfront/cfn/cache-policy/sim-cfn-cf-cache-policy-creator.d.ts +24 -0
  69. package/dist/service/cloudfront/cfn/cache-policy/sim-cfn-cf-cache-policy-creator.js +32 -0
  70. package/dist/service/cloudfront/cfn/distro/{sim-cfn-cf-distro-response-headers-policy.d.ts → sim-cfn-cf-distro-behavior-policies.d.ts} +8 -7
  71. package/dist/service/cloudfront/cfn/distro/{sim-cfn-cf-distro-response-headers-policy.js → sim-cfn-cf-distro-behavior-policies.js} +11 -37
  72. package/dist/service/cloudfront/cfn/distro/sim-cfn-cf-distro-creator.d.ts +6 -5
  73. package/dist/service/cloudfront/cfn/distro/sim-cfn-cf-distro-creator.js +8 -7
  74. package/dist/service/cloudfront/cfn/distro/sim-cfn-cf-distro-policy-drops.d.ts +23 -0
  75. package/dist/service/cloudfront/cfn/distro/sim-cfn-cf-distro-policy-drops.js +50 -0
  76. package/dist/service/cloudfront/cfn/sim-cfn-cloudfront-resource-factory.d.ts +1 -0
  77. package/dist/service/cloudfront/cfn/sim-cfn-cloudfront-resource-factory.js +10 -0
  78. package/dist/service/cloudfront/command/create-distribution/create-distribution.command.d.ts +1 -0
  79. package/dist/service/cloudfront/command/create-distribution/create-distribution.handler.d.ts +2 -0
  80. package/dist/service/cloudfront/command/create-function/create-function.handler.js +7 -1
  81. package/dist/service/cloudfront/command/function/sim-cf-describe-function.d.ts +22 -0
  82. package/dist/service/cloudfront/command/function/sim-cf-describe-function.js +32 -0
  83. package/dist/service/cloudfront/command/function/sim-cf-function-access.d.ts +48 -0
  84. package/dist/service/cloudfront/command/function/sim-cf-function-access.js +48 -0
  85. package/dist/service/cloudfront/command/function/sim-cf-function-command.types.d.ts +97 -0
  86. package/dist/service/cloudfront/command/function/sim-cf-function-command.types.js +10 -0
  87. package/dist/service/cloudfront/command/function/sim-cf-function-lookup.d.ts +12 -0
  88. package/dist/service/cloudfront/command/function/sim-cf-function-lookup.js +21 -0
  89. package/dist/service/cloudfront/command/function/sim-cf-function-summary.d.ts +11 -0
  90. package/dist/service/cloudfront/command/function/sim-cf-function-summary.js +30 -0
  91. package/dist/service/cloudfront/command/function/sim-cf-get-function.d.ts +23 -0
  92. package/dist/service/cloudfront/command/function/sim-cf-get-function.js +33 -0
  93. package/dist/service/cloudfront/command/function/sim-cf-list-functions.d.ts +22 -0
  94. package/dist/service/cloudfront/command/function/sim-cf-list-functions.js +38 -0
  95. package/dist/service/cloudfront/command/update-distribution/update-distribution.handler.d.ts +2 -0
  96. package/dist/service/cloudfront/distribution/configurator/sim-cf-behavior-cache-policy.d.ts +18 -0
  97. package/dist/service/cloudfront/distribution/configurator/sim-cf-behavior-cache-policy.js +30 -0
  98. package/dist/service/cloudfront/distribution/configurator/sim-cf-behavior-policies.d.ts +28 -0
  99. package/dist/service/cloudfront/distribution/configurator/sim-cf-behavior-policies.js +41 -0
  100. package/dist/service/cloudfront/distribution/configurator/sim-cf-behavior-properties.d.ts +2 -2
  101. package/dist/service/cloudfront/distribution/configurator/sim-cf-behavior-properties.js +6 -3
  102. package/dist/service/cloudfront/distribution/configurator/sim-cf-behavior-response-headers-policy.d.ts +0 -10
  103. package/dist/service/cloudfront/distribution/configurator/sim-cf-behavior-response-headers-policy.js +0 -19
  104. package/dist/service/cloudfront/distribution/configurator/sim-cf-distribution-configurator.factory.d.ts +2 -0
  105. package/dist/service/cloudfront/distribution/configurator/sim-cf-distribution-configurator.factory.js +4 -2
  106. package/dist/service/cloudfront/distribution/configurator/sim-cloud-front-behavior-configurator.d.ts +3 -3
  107. package/dist/service/cloudfront/distribution/configurator/sim-cloud-front-behavior-configurator.js +5 -5
  108. package/dist/service/cloudfront/distribution/configurator/sim-cloud-front-distribution-configurator.d.ts +3 -3
  109. package/dist/service/cloudfront/distribution/configurator/sim-cloud-front-distribution-configurator.js +4 -4
  110. package/dist/service/cloudfront/distribution/sim-cf-distribution-configuration-state.d.ts +3 -1
  111. package/dist/service/cloudfront/distribution/sim-cf-distribution-reconfigurer.d.ts +2 -0
  112. package/dist/service/cloudfront/error/sim-cloudfront.error.d.ts +21 -0
  113. package/dist/service/cloudfront/error/sim-cloudfront.error.js +25 -0
  114. package/dist/service/cloudfront/key-value-store/sim-cf-key-value-store-access.js +2 -10
  115. package/dist/service/cloudfront/key-value-store/sim-cf-key-value-store.js +2 -1
  116. package/dist/service/cloudfront/sdk/sim-cloudfront-sdk-command-router.js +12 -0
  117. package/dist/service/cloudfront/sim-cf-authorize.d.ts +16 -0
  118. package/dist/service/cloudfront/sim-cf-authorize.js +19 -0
  119. package/dist/service/cloudfront/sim-cf-etag.d.ts +7 -0
  120. package/dist/service/cloudfront/sim-cf-etag.js +10 -0
  121. package/dist/service/cloudfront/sim-cloudfront-commands.d.ts +8 -12
  122. package/dist/service/cloudfront/sim-cloudfront-commands.js +8 -17
  123. package/dist/service/cloudfront/sim-cloudfront-policies.d.ts +43 -0
  124. package/dist/service/cloudfront/sim-cloudfront-policies.js +53 -0
  125. package/dist/service/cloudfront/sim-cloudfront.d.ts +13 -17
  126. package/dist/service/cloudfront/sim-cloudfront.js +22 -25
  127. package/dist/service/ecr/authorize/sim-ecr-authorizer.d.ts +31 -0
  128. package/dist/service/ecr/authorize/sim-ecr-authorizer.js +38 -0
  129. package/dist/service/ecr/cfn/repository/sim-cfn-ecr-repository-creator.d.ts +11 -2
  130. package/dist/service/ecr/cfn/repository/sim-cfn-ecr-repository-creator.js +11 -2
  131. package/dist/service/ecr/cfn/sim-ecr-cfn-resource-factory.d.ts +4 -2
  132. package/dist/service/ecr/cfn/sim-ecr-cfn-resource-factory.js +8 -9
  133. package/dist/service/ecr/cfn/sim-ecr-cfn-unsupported-resource.d.ts +7 -0
  134. package/dist/service/ecr/cfn/sim-ecr-cfn-unsupported-resource.js +9 -0
  135. package/dist/service/ecr/sim-ecr.d.ts +7 -0
  136. package/dist/service/ecr/sim-ecr.js +16 -4
  137. package/dist/service/ecs/command/authorize/sim-ecs-authorizer.d.ts +12 -0
  138. package/dist/service/ecs/command/authorize/sim-ecs-authorizer.js +21 -0
  139. package/dist/service/ecs/command/register-task-definition/register-task-definition.handler.js +1 -0
  140. package/dist/service/ecs/sim-ecs-service-principal.d.ts +8 -0
  141. package/dist/service/ecs/sim-ecs-service-principal.js +8 -0
  142. package/dist/service/eventbridge/command/authorize/sim-event-bridge-authorizer.d.ts +12 -0
  143. package/dist/service/eventbridge/command/authorize/sim-event-bridge-authorizer.js +21 -0
  144. package/dist/service/eventbridge/command/rule/sim-event-bridge-rule-access.d.ts +13 -0
  145. package/dist/service/eventbridge/command/rule/sim-event-bridge-rule-access.js +9 -0
  146. package/dist/service/eventbridge/command/target/sim-event-bridge-put-targets.js +1 -0
  147. package/dist/service/firehose/command/authorize/sim-firehose-authorizer.d.ts +12 -0
  148. package/dist/service/firehose/command/authorize/sim-firehose-authorizer.js +20 -0
  149. package/dist/service/firehose/command/sim-firehose-delivery-stream-access.d.ts +4 -0
  150. package/dist/service/firehose/command/sim-firehose-delivery-stream-access.js +6 -0
  151. package/dist/service/firehose/command/stream/sim-firehose-create-delivery-stream.js +3 -1
  152. package/dist/service/firehose/sim-firehose-service-principal.d.ts +8 -0
  153. package/dist/service/firehose/sim-firehose-service-principal.js +8 -0
  154. package/dist/service/firehose/source/sim-firehose-source.d.ts +5 -0
  155. package/dist/service/firehose/source/sim-firehose-source.js +5 -0
  156. package/dist/service/glue/cfn/database/sim-cfn-glue-database-properties.js +2 -2
  157. package/dist/service/glue/cfn/sim-cfn-glue-generated-name.d.ts +10 -0
  158. package/dist/service/glue/cfn/sim-cfn-glue-generated-name.js +25 -0
  159. package/dist/service/glue/cfn/table/sim-cfn-glue-table-properties.js +2 -3
  160. package/dist/service/iam/authorize/allow/sim-iam-allow-requirement.d.ts +33 -2
  161. package/dist/service/iam/authorize/allow/sim-iam-allow-requirement.js +33 -2
  162. package/dist/service/iam/authorize/allow/sim-iam-allow-statements.d.ts +4 -3
  163. package/dist/service/iam/authorize/allow/sim-iam-allow-statements.js +9 -3
  164. package/dist/service/iam/authorize/context/sim-iam-auth-z-allow-requirement.d.ts +14 -5
  165. package/dist/service/iam/authorize/context/sim-iam-auth-z-allow-requirement.js +16 -12
  166. package/dist/service/iam/authorize/context/sim-iam-auth-z-caller-context-builder.d.ts +36 -7
  167. package/dist/service/iam/authorize/context/sim-iam-auth-z-caller-context-builder.js +22 -11
  168. package/dist/service/iam/authorize/context/sim-iam-auth-z-context-builder.d.ts +10 -71
  169. package/dist/service/iam/authorize/context/sim-iam-auth-z-context-builder.js +7 -2
  170. package/dist/service/iam/authorize/context/sim-iam-auth-z-input.d.ts +87 -0
  171. package/dist/service/iam/authorize/context/sim-iam-auth-z-input.js +1 -0
  172. package/dist/service/iam/authorize/match/sim-iam-policy-principal-matcher.d.ts +21 -3
  173. package/dist/service/iam/authorize/match/sim-iam-policy-principal-matcher.js +34 -10
  174. package/dist/service/iam/authorize/match/sim-iam-principal-match.d.ts +29 -8
  175. package/dist/service/iam/authorize/match/sim-iam-principal-match.js +38 -13
  176. package/dist/service/iam/authorize/pass-role/sim-iam-pass-role-authorizer.d.ts +74 -0
  177. package/dist/service/iam/authorize/pass-role/sim-iam-pass-role-authorizer.js +92 -0
  178. package/dist/service/iam/authorize/sim-iam-account-auth-z.d.ts +21 -5
  179. package/dist/service/iam/authorize/sim-iam-account-auth-z.js +7 -3
  180. package/dist/service/iam/authorize/sim-iam-authorizer.d.ts +5 -2
  181. package/dist/service/iam/authorize/sim-iam-authorizer.js +3 -1
  182. package/dist/service/iam/authorize/sim-iam-inter-service-auth-z.d.ts +1 -1
  183. package/dist/service/iam/authorize/sim-iam-wildcard.js +39 -4
  184. package/dist/service/iam/cfn/managed-policy/sim-cfn-iam-managed-policy-creator.js +2 -1
  185. package/dist/service/iam/cfn/name/sim-cfn-iam-generated-name.d.ts +16 -0
  186. package/dist/service/iam/cfn/name/sim-cfn-iam-generated-name.js +36 -0
  187. package/dist/service/iam/cfn/role/sim-cfn-iam-role-properties-parser.js +9 -8
  188. package/dist/service/iam/cfn/sim-cfn-iam-optional-string.d.ts +17 -0
  189. package/dist/service/iam/cfn/sim-cfn-iam-optional-string.js +17 -0
  190. package/dist/service/iam/cfn/user/sim-cfn-iam-user-properties-parser.js +4 -8
  191. package/dist/service/iam/command/policy/create-policy/create-policy-input-resolver.js +2 -0
  192. package/dist/service/iam/command/policy/put-role-policy/put-role-policy.handler.js +5 -0
  193. package/dist/service/iam/command/policy/put-user-policy/put-user-policy.handler.js +5 -0
  194. package/dist/service/iam/command/role/create-role/create-role.handler.js +4 -1
  195. package/dist/service/iam/error/sim-iam.error.d.ts +11 -0
  196. package/dist/service/iam/error/sim-iam.error.js +13 -0
  197. package/dist/service/iam/index.d.ts +1 -0
  198. package/dist/service/iam/policy/sim-iam-policy-document-of-size.d.ts +18 -0
  199. package/dist/service/iam/policy/sim-iam-policy-document-of-size.js +41 -0
  200. package/dist/service/iam/role/sim-iam-deploy-role.d.ts +56 -0
  201. package/dist/service/iam/role/sim-iam-deploy-role.js +50 -0
  202. package/dist/service/iam/role/sim-iam-role-with-policy.factory.d.ts +11 -0
  203. package/dist/service/iam/role/sim-iam-role-with-policy.factory.js +4 -2
  204. package/dist/service/iam/sim-iam-account-parts.d.ts +16 -0
  205. package/dist/service/iam/sim-iam-account-parts.js +2 -0
  206. package/dist/service/iam/sim-iam.d.ts +12 -2
  207. package/dist/service/iam/sim-iam.js +12 -0
  208. package/dist/service/iam/validate/size/sim-iam-policy-document-size.d.ts +57 -0
  209. package/dist/service/iam/validate/size/sim-iam-policy-document-size.js +71 -0
  210. package/dist/service/kms/command/authorize/sim-kms-authorizer.d.ts +3 -1
  211. package/dist/service/kms/command/authorize/sim-kms-authorizer.js +4 -1
  212. package/dist/service/kms/command/sim-kms-request-options.d.ts +11 -0
  213. package/dist/service/kms/key/sim-kms-key-policy.d.ts +5 -4
  214. package/dist/service/kms/key/sim-kms-key-policy.js +4 -3
  215. package/dist/service/lambda/cfn/function/sim-cfn-lambda-function-properties-parser.d.ts +2 -2
  216. package/dist/service/lambda/cfn/function/sim-cfn-lambda-function-properties-parser.js +4 -3
  217. package/dist/service/lambda/cfn/function/sim-cfn-lambda-generated-function-name.d.ts +10 -0
  218. package/dist/service/lambda/cfn/function/sim-cfn-lambda-generated-function-name.js +25 -0
  219. package/dist/service/lambda/command/authorize/sim-lambda-resource-policies.d.ts +1 -1
  220. package/dist/service/lambda/command/create-function/create-function-authorizer.d.ts +9 -2
  221. package/dist/service/lambda/command/create-function/create-function-authorizer.js +16 -2
  222. package/dist/service/lambda/command/create-function/create-function.handler.js +1 -1
  223. package/dist/service/lambda/command/update-function-configuration/update-function-configuration-authorizer.d.ts +7 -2
  224. package/dist/service/lambda/command/update-function-configuration/update-function-configuration-authorizer.js +14 -2
  225. package/dist/service/lambda/command/update-function-configuration/update-function-configuration.handler.js +1 -1
  226. package/dist/service/lambda/function/logging/sim-lambda-function-logging.d.ts +10 -1
  227. package/dist/service/lambda/function/logging/sim-lambda-function-logging.js +25 -1
  228. package/dist/service/lambda/function/logging/sim-lambda-invoke-error-log.d.ts +13 -0
  229. package/dist/service/lambda/function/logging/sim-lambda-invoke-error-log.js +18 -0
  230. package/dist/service/lambda/sim-lambda-service-principal.d.ts +7 -0
  231. package/dist/service/lambda/sim-lambda-service-principal.js +7 -0
  232. package/dist/service/logs/cfn/delivery/sim-cfn-delivery-authorization.d.ts +46 -0
  233. package/dist/service/logs/cfn/delivery/sim-cfn-delivery-authorization.js +49 -0
  234. package/dist/service/logs/cfn/delivery/sim-cfn-delivery-creator.d.ts +5 -0
  235. package/dist/service/logs/cfn/delivery/sim-cfn-delivery-creator.js +6 -0
  236. package/dist/service/logs/cfn/delivery/sim-cfn-delivery-destination-creator.d.ts +5 -0
  237. package/dist/service/logs/cfn/delivery/sim-cfn-delivery-destination-creator.js +6 -0
  238. package/dist/service/logs/cfn/delivery/sim-cfn-delivery-source-creator.d.ts +5 -0
  239. package/dist/service/logs/cfn/delivery/sim-cfn-delivery-source-creator.js +6 -0
  240. package/dist/service/logs/cfn/group/sim-cfn-log-group-creator.d.ts +9 -2
  241. package/dist/service/logs/cfn/group/sim-cfn-log-group-creator.js +34 -5
  242. package/dist/service/logs/cfn/sim-logs-cfn-resource-deleter.js +1 -1
  243. package/dist/service/logs/cfn/sim-logs-cfn-resource-factory.d.ts +4 -0
  244. package/dist/service/logs/cfn/sim-logs-cfn-resource-factory.js +9 -4
  245. package/dist/service/logs/sim-logs-commands.d.ts +3 -0
  246. package/dist/service/logs/sim-logs-commands.js +4 -0
  247. package/dist/service/logs/sim-logs.js +6 -1
  248. package/dist/service/s3/cfn/bucket/sim-cfn-s3-bucket-creator.d.ts +2 -2
  249. package/dist/service/s3/cfn/bucket/sim-cfn-s3-bucket-creator.js +4 -3
  250. package/dist/service/s3/cfn/bucket/sim-cfn-s3-bucket-generated-name.d.ts +12 -0
  251. package/dist/service/s3/cfn/bucket/sim-cfn-s3-bucket-generated-name.js +22 -0
  252. package/dist/service/s3/command/authorize/sim-s3-bucket-resource-policies.d.ts +1 -1
  253. package/dist/service/scheduler/command/authorize/sim-scheduler-authorizer.d.ts +10 -1
  254. package/dist/service/scheduler/command/authorize/sim-scheduler-authorizer.js +19 -1
  255. package/dist/service/scheduler/command/schedule/sim-scheduler-create-schedule.js +1 -0
  256. package/dist/service/scheduler/command/schedule/sim-scheduler-schedule-access.d.ts +8 -0
  257. package/dist/service/scheduler/command/schedule/sim-scheduler-schedule-access.js +9 -0
  258. package/dist/service/scheduler/command/schedule/sim-scheduler-update-schedule.js +1 -0
  259. package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-generated-name.d.ts +15 -0
  260. package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-generated-name.js +22 -0
  261. package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-properties.d.ts +4 -10
  262. package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-properties.js +13 -31
  263. package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-value.d.ts +17 -0
  264. package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-value.js +30 -0
  265. package/dist/service/secretsmanager/secret/sim-secrets-manager-secret-name.d.ts +6 -0
  266. package/dist/service/secretsmanager/secret/sim-secrets-manager-secret-name.js +8 -3
  267. package/dist/service/sns/command/authorize/sim-sns-topic-resource-policies.d.ts +1 -1
  268. package/dist/service/sqs/command/authorize/sim-sqs-queue-resource-policies.d.ts +1 -1
  269. package/dist/service/ssm/cfn/parameter/sim-cfn-ssm-generated-parameter-name.d.ts +16 -0
  270. package/dist/service/ssm/cfn/parameter/sim-cfn-ssm-generated-parameter-name.js +23 -0
  271. package/dist/service/ssm/cfn/parameter/sim-cfn-ssm-parameter-properties.d.ts +2 -4
  272. package/dist/service/ssm/cfn/parameter/sim-cfn-ssm-parameter-properties.js +5 -5
  273. package/dist/service/ssm/parameter/sim-ssm-kms-crypto.d.ts +6 -4
  274. package/dist/service/ssm/parameter/sim-ssm-kms-crypto.js +4 -4
  275. package/dist/service/ssm/parameter/sim-ssm-parameter-arn.d.ts +17 -0
  276. package/dist/service/ssm/parameter/sim-ssm-parameter-arn.js +20 -0
  277. package/dist/service/ssm/parameter/sim-ssm-parameter-kms.d.ts +15 -9
  278. package/dist/service/ssm/parameter/sim-ssm-parameter-kms.js +20 -10
  279. package/dist/service/ssm/parameter/sim-ssm-parameter-name.js +3 -12
  280. package/dist/service/stepfunctions/command/authorize/sim-step-functions-authorizer.d.ts +41 -0
  281. package/dist/service/stepfunctions/command/authorize/sim-step-functions-authorizer.js +64 -0
  282. package/dist/service/stepfunctions/command/machine/sim-state-machine-create.d.ts +5 -2
  283. package/dist/service/stepfunctions/command/machine/sim-state-machine-create.js +6 -2
  284. package/dist/service/stepfunctions/command/machine/sim-state-machine-delete.d.ts +26 -0
  285. package/dist/service/stepfunctions/command/machine/sim-state-machine-delete.js +29 -0
  286. package/dist/service/stepfunctions/command/machine/sim-state-machine-lookup.d.ts +7 -0
  287. package/dist/service/stepfunctions/command/machine/sim-state-machine-lookup.js +12 -3
  288. package/dist/service/stepfunctions/command/machine/sim-state-machine-update.d.ts +19 -0
  289. package/dist/service/stepfunctions/command/machine/{sim-state-machine-writes.js → sim-state-machine-update.js} +3 -14
  290. package/dist/service/stepfunctions/sim-step-functions.d.ts +8 -2
  291. package/dist/service/stepfunctions/sim-step-functions.js +26 -9
  292. package/dist/service/sts/auth-z/assume-role-auth-z-coordinator.d.ts +9 -2
  293. package/dist/service/sts/auth-z/assume-role-auth-z-coordinator.js +2 -2
  294. package/dist/service/sts/auth-z/assume-role-source-account-auth-z.d.ts +10 -4
  295. package/dist/service/sts/auth-z/assume-role-source-account-auth-z.js +8 -7
  296. package/dist/service/sts/auth-z/assume-role-target-auth-z.d.ts +2 -2
  297. package/dist/service/sts/auth-z/assume-role-trust-policy-authorizer.d.ts +6 -2
  298. package/dist/service/sts/auth-z/assume-role-trust-policy-authorizer.js +14 -11
  299. package/dist/service/sts/command/assume-role/assume-role.handler.d.ts +10 -1
  300. package/dist/service/sts/command/assume-role/assume-role.handler.js +6 -2
  301. package/dist/service/sts/command/get-caller-identity/get-caller-identity.handler.d.ts +10 -1
  302. package/dist/service/sts/command/get-caller-identity/get-caller-identity.handler.js +5 -1
  303. package/dist/service/sts/service-role/sim-service-role.js +5 -1
  304. package/dist/service/sts/sim-sts.d.ts +18 -1
  305. package/dist/service/sts/sim-sts.js +8 -0
  306. package/dist/util/memo/bounded-memo.d.ts +36 -0
  307. package/dist/util/memo/bounded-memo.js +58 -0
  308. package/docs/README.md +54 -0
  309. package/docs/ai-skill/README.md +69 -0
  310. package/docs/cli/README.md +284 -0
  311. package/docs/factories/README.md +140 -0
  312. package/docs/lint/README.md +176 -0
  313. package/docs/non-aws-dependencies/README.md +261 -0
  314. package/docs/sdk/README.md +274 -0
  315. package/docs/serve/README.md +1126 -0
  316. package/docs/services/acm/README.md +823 -0
  317. package/docs/services/apigateway/README.md +1919 -0
  318. package/docs/services/apigatewayv2/README.md +2678 -0
  319. package/docs/services/athena/README.md +1044 -0
  320. package/docs/services/bedrock/README.md +350 -0
  321. package/docs/services/cloudformation/README.md +3625 -0
  322. package/docs/services/cloudfront/README.md +3098 -0
  323. package/docs/services/cloudwatch/README.md +422 -0
  324. package/docs/services/cognito/README.md +4924 -0
  325. package/docs/services/dynamodb/README.md +3785 -0
  326. package/docs/services/ecr/README.md +302 -0
  327. package/docs/services/ecs/README.md +2021 -0
  328. package/docs/services/elbv2/README.md +1901 -0
  329. package/docs/services/eventbridge/README.md +1118 -0
  330. package/docs/services/firehose/README.md +811 -0
  331. package/docs/services/glue/README.md +507 -0
  332. package/docs/services/iam/README.md +1578 -0
  333. package/docs/services/kinesis/README.md +455 -0
  334. package/docs/services/kms/README.md +664 -0
  335. package/docs/services/lambda/README.md +3719 -0
  336. package/docs/services/logs/README.md +882 -0
  337. package/docs/services/organizations/README.md +574 -0
  338. package/docs/services/personalize/README.md +835 -0
  339. package/docs/services/rekognition/README.md +1070 -0
  340. package/docs/services/route53/README.md +1783 -0
  341. package/docs/services/s3/README.md +3029 -0
  342. package/docs/services/scheduler/README.md +814 -0
  343. package/docs/services/secretsmanager/README.md +670 -0
  344. package/docs/services/ses/README.md +1013 -0
  345. package/docs/services/sns/README.md +1904 -0
  346. package/docs/services/sqs/README.md +1185 -0
  347. package/docs/services/ssm/README.md +1096 -0
  348. package/docs/services/stepfunctions/README.md +1341 -0
  349. package/docs/services/sts/README.md +245 -0
  350. package/docs/services/wafv2/README.md +1380 -0
  351. package/docs/terraform/README.md +248 -0
  352. package/docs/time/README.md +284 -0
  353. package/llms.txt +59 -0
  354. package/package.json +7 -4
  355. package/dist/service/stepfunctions/command/machine/sim-state-machine-writes.d.ts +0 -25
@@ -0,0 +1,1380 @@
1
+ # Simulated WAFv2
2
+
3
+ Yulin includes a simulated AWS WAFv2 for tests and local development. It holds web ACLs, IP sets and
4
+ regex pattern sets, and it evaluates a request against a web ACL's rules to reach a decision. A test
5
+ can assert that a request to `/admin` is blocked and one to `/` is allowed, without an AWS account
6
+ and without a distribution in front of anything.
7
+
8
+ A web ACL can also go in front of what serves the requests. A simulated API Gateway REST API stage
9
+ and a simulated Cognito user pool each take one through `AssociateWebACL`, and a simulated
10
+ CloudFront distribution takes one through its own `WebACLId`. The requests that stage, pool or
11
+ distribution serves are then put through the web ACL's rules.
12
+
13
+ WAFv2 specific types are imported from the `@kensio/yulin/wafv2` subpath.
14
+
15
+ ## Deciding what happens to a request
16
+
17
+ `evaluateRequest` puts one request through a web ACL. It takes the web ACL's ARN and an ordinary
18
+ `Request`, and answers with the decision.
19
+
20
+ ```typescript sim-wafv2-evaluate
21
+ /**
22
+ * Blocking requests to an admin path with a simulated web ACL.
23
+ */
24
+
25
+ import { CreateWebACLCommand } from "@aws-sdk/client-wafv2";
26
+
27
+ import { SimAws } from "@kensio/yulin";
28
+
29
+ const waf = new SimAws().wafV2();
30
+
31
+ const created = await waf.createWebAcl(
32
+ new CreateWebACLCommand({
33
+ Name: "api-acl",
34
+ Scope: "REGIONAL",
35
+ DefaultAction: { Allow: {} },
36
+ VisibilityConfig: {
37
+ SampledRequestsEnabled: false,
38
+ CloudWatchMetricsEnabled: false,
39
+ MetricName: "api",
40
+ },
41
+ Rules: [
42
+ {
43
+ Name: "block-admin",
44
+ Priority: 0,
45
+ Action: { Block: {} },
46
+ Statement: {
47
+ ByteMatchStatement: {
48
+ FieldToMatch: { UriPath: {} },
49
+ PositionalConstraint: "STARTS_WITH",
50
+ SearchString: Buffer.from("/admin"),
51
+ TextTransformations: [{ Priority: 0, Type: "LOWERCASE" }],
52
+ },
53
+ },
54
+ VisibilityConfig: {
55
+ SampledRequestsEnabled: false,
56
+ CloudWatchMetricsEnabled: false,
57
+ MetricName: "block-admin",
58
+ },
59
+ },
60
+ ],
61
+ }),
62
+ );
63
+
64
+ const webAclArn = created.Summary!.ARN;
65
+
66
+ const blocked = waf.evaluateRequest({
67
+ webAclArn,
68
+ request: new Request("https://example.test/admin/users"),
69
+ });
70
+ const allowed = waf.evaluateRequest({
71
+ webAclArn,
72
+ request: new Request("https://example.test/"),
73
+ });
74
+
75
+ // "BLOCK" "block-admin" 403
76
+ console.log(
77
+ blocked.action,
78
+ blocked.terminatingRuleName,
79
+ blocked.blocked?.statusCode,
80
+ );
81
+
82
+ // "ALLOW" undefined
83
+ console.log(allowed.action, allowed.terminatingRuleName);
84
+ ```
85
+
86
+ The decision names the rule that reached it. A request no rule claims gets the web ACL's
87
+ `DefaultAction`, and `terminatingRuleName` is then absent.
88
+
89
+ `simWafBlockedHttpResponse` turns a blocked decision into the `Response` a client would receive,
90
+ carrying the status, the body and any headers the rule named.
91
+
92
+ ## Rules run in priority order
93
+
94
+ Rules are evaluated in ascending `Priority` and not in the order the list was written. The first
95
+ rule that matches and carries a terminating action (`Allow` or `Block`) decides the request.
96
+
97
+ A `Count` action records the match and lets the next rule have a look. That is how a rule is staged
98
+ before it is turned on, and `countedRuleNames` is what a test asserts against.
99
+
100
+ ```typescript sim-wafv2-count
101
+ /**
102
+ * Staging a rule in count mode before turning it on.
103
+ */
104
+
105
+ import { CreateWebACLCommand } from "@aws-sdk/client-wafv2";
106
+
107
+ import { SimAws } from "@kensio/yulin";
108
+
109
+ const waf = new SimAws().wafV2();
110
+
111
+ const visibility = {
112
+ SampledRequestsEnabled: false,
113
+ CloudWatchMetricsEnabled: false,
114
+ MetricName: "api",
115
+ };
116
+
117
+ const created = await waf.createWebAcl(
118
+ new CreateWebACLCommand({
119
+ Name: "api-acl",
120
+ Scope: "REGIONAL",
121
+ DefaultAction: { Allow: {} },
122
+ VisibilityConfig: visibility,
123
+ Rules: [
124
+ {
125
+ Name: "watch-uploads",
126
+ Priority: 0,
127
+ Action: { Count: {} },
128
+ Statement: {
129
+ SizeConstraintStatement: {
130
+ FieldToMatch: { Body: { OversizeHandling: "CONTINUE" } },
131
+ ComparisonOperator: "GT",
132
+ Size: 1024,
133
+ TextTransformations: [{ Priority: 0, Type: "NONE" }],
134
+ },
135
+ },
136
+ VisibilityConfig: visibility,
137
+ },
138
+ ],
139
+ }),
140
+ );
141
+
142
+ const decision = waf.evaluateRequest({
143
+ webAclArn: created.Summary!.ARN,
144
+ request: new Request("https://example.test/upload", { method: "POST" }),
145
+ body: new TextEncoder().encode("x".repeat(2048)),
146
+ });
147
+
148
+ // "ALLOW" [ 'watch-uploads' ]
149
+ console.log(decision.action, decision.countedRuleNames);
150
+ ```
151
+
152
+ The body is passed in already read. A request body is a stream that cannot be consumed twice, and
153
+ whatever serves the request has usually read it by the time WAF gets a look.
154
+
155
+ ## What a statement can inspect
156
+
157
+ A statement reads one part of the request, applies the rule's text transformations to it, and tests
158
+ what comes out.
159
+
160
+ The parts a statement can be pointed at are `UriPath`, `QueryString`, `SingleQueryArgument`,
161
+ `AllQueryArguments`, `SingleHeader`, `Headers`, `Cookies`, `Method` and `Body`. `Headers` and
162
+ `Cookies` take a `MatchPattern` selecting which of them to read and a `MatchScope` of `KEY`, `VALUE`
163
+ or `ALL`.
164
+
165
+ The transformations are `NONE`, `LOWERCASE`, `URL_DECODE`, `COMPRESS_WHITE_SPACE` and
166
+ `HTML_ENTITY_DECODE`. They run in ascending `Priority`, so lowercasing after decoding is a different
167
+ rule from decoding after lowercasing.
168
+
169
+ The tests are `ByteMatchStatement` (with `EXACTLY`, `STARTS_WITH`, `ENDS_WITH`, `CONTAINS` and
170
+ `CONTAINS_WORD`), `RegexMatchStatement`, `RegexPatternSetReferenceStatement` and
171
+ `SizeConstraintStatement`. `AndStatement`, `OrStatement` and `NotStatement` join and negate them,
172
+ and they nest.
173
+
174
+ An `AndStatement` or an `OrStatement` needs at least two statements to join. Real WAF answers a web
175
+ ACL holding one that joins fewer with `OR_STATEMENT` and a minimum threshold, refusing the whole
176
+ resource, and `CreateWebACL` here refuses it too.
177
+
178
+ Matching is case sensitive, as it is on AWS. A rule that means to ignore case says so with a
179
+ `LOWERCASE` transformation and a lower case search string.
180
+
181
+ WAF stops reading a body, a header set or a cookie set at 8 KB. The rule's `OversizeHandling` says
182
+ what content past that point counts as. `MATCH` and `NO_MATCH` settle the statement without looking,
183
+ and `CONTINUE` inspects as much as WAF would have read.
184
+
185
+ ## Rate limiting
186
+
187
+ `RateBasedStatement` counts the requests one client makes and applies the rule's action once the
188
+ count goes past `Limit`. A test sends requests until the rule trips, then moves the simulated clock
189
+ past the window to watch it let go again.
190
+
191
+ ```typescript sim-wafv2-rate-limit
192
+ /**
193
+ * Limiting how often one client may ask to create an account.
194
+ */
195
+
196
+ import { CreateWebACLCommand } from "@aws-sdk/client-wafv2";
197
+
198
+ import { SimAws } from "@kensio/yulin";
199
+
200
+ const simAws = new SimAws();
201
+ const waf = simAws.wafV2();
202
+
203
+ const visibility = {
204
+ SampledRequestsEnabled: false,
205
+ CloudWatchMetricsEnabled: false,
206
+ MetricName: "pool",
207
+ };
208
+
209
+ const created = await waf.createWebAcl(
210
+ new CreateWebACLCommand({
211
+ Name: "pool-acl",
212
+ Scope: "REGIONAL",
213
+ DefaultAction: { Allow: {} },
214
+ VisibilityConfig: visibility,
215
+ Rules: [
216
+ {
217
+ Name: "sign-up-rate",
218
+ Priority: 0,
219
+ Action: { Block: {} },
220
+ Statement: {
221
+ RateBasedStatement: {
222
+ Limit: 10,
223
+ EvaluationWindowSec: 300,
224
+ AggregateKeyType: "IP",
225
+ ScopeDownStatement: {
226
+ ByteMatchStatement: {
227
+ FieldToMatch: { UriPath: {} },
228
+ PositionalConstraint: "STARTS_WITH",
229
+ SearchString: Buffer.from("/signup"),
230
+ TextTransformations: [{ Priority: 0, Type: "LOWERCASE" }],
231
+ },
232
+ },
233
+ },
234
+ },
235
+ VisibilityConfig: { ...visibility, MetricName: "sign-up-rate" },
236
+ },
237
+ ],
238
+ }),
239
+ );
240
+
241
+ const webAclArn = created.Summary!.ARN;
242
+
243
+ const signUp = (): string =>
244
+ waf.evaluateRequest({
245
+ webAclArn,
246
+ request: new Request("https://pool.example.test/signup"),
247
+ }).action;
248
+
249
+ const decisions = Array.from({ length: 11 }, signUp);
250
+
251
+ // "ALLOW" "BLOCK"
252
+ console.log(decisions[9], decisions[10]);
253
+
254
+ const login = waf.evaluateRequest({
255
+ webAclArn,
256
+ request: new Request("https://pool.example.test/login"),
257
+ });
258
+
259
+ // "ALLOW"
260
+ console.log(login.action);
261
+
262
+ await simAws.clock().advanceBy({ minutes: 6 });
263
+
264
+ // "ALLOW"
265
+ console.log(signUp());
266
+ ```
267
+
268
+ `Limit` is how many requests one aggregation instance may make inside the window. AWS holds it
269
+ between 10 and 2,000,000,000. The request that takes the count past the limit gets the rule's
270
+ action, and the ones under it carry on to the next rule. A `Count` action records the match and
271
+ lets evaluation continue, the way it does for every other statement kind.
272
+
273
+ `EvaluationWindowSec` is 60, 120, 300 or 600 seconds. A statement naming none counts over 300. The
274
+ window is measured against [simulated time](https://yulinsim.dev/time/), so `advanceBy` past it drops
275
+ what the rule counted.
276
+
277
+ `AggregateKeyType` is `IP` or `CONSTANT`. `IP` counts each client address on its own. Every request
278
+ in this simulation reports `127.0.0.1`, leaving a web ACL with one client for the whole simulation.
279
+ That is the case a rate limiting test is written about anyway (one client, sending until the rule
280
+ trips), and it behaves here as it does on AWS. `CONSTANT` counts every request the statement sees
281
+ together, and AWS requires a `ScopeDownStatement` alongside it to say which requests those are.
282
+
283
+ A `ScopeDownStatement` narrows what the rule counts. Every statement kind in
284
+ [What a statement can inspect](#what-a-statement-can-inspect) nests inside one. A request the
285
+ scope-down statement leaves alone is neither counted nor limited. That is what keeps a limit on
286
+ `/signup` off the rest of a site.
287
+
288
+ The counts belong to the rule. Writing a new set of rules over a web ACL with `UpdateWebACL` starts
289
+ them from nothing, as it does on AWS.
290
+
291
+ A rate limit is the whole of a rule's statement, as it is on real WAFv2. A rule naming another
292
+ statement kind beside it, and a rate limit nested inside an `AndStatement` or a `NotStatement`, are
293
+ both refused where the rule is written.
294
+
295
+ ## The AWS managed rule groups
296
+
297
+ Three of the AWS managed rule groups are simulated, so a stack that turns them on deploys and its
298
+ traffic can be tested against them.
299
+
300
+ - `AWSManagedRulesCommonRuleSet`, the core rule set, 22 rules.
301
+ - `AWSManagedRulesKnownBadInputsRuleSet`, 11 rules.
302
+ - `AWSManagedRulesAdminProtectionRuleSet`, one rule.
303
+
304
+ A group evaluates its rules in the order AWS documents them, adds the documented
305
+ `awswaf:managed:aws:*` label to a request a rule claims, and blocks by that rule's action. The
306
+ labels are on the decision, and they are what says which rule inside a group claimed the request.
307
+
308
+ ```typescript sim-wafv2-managed-rule-group
309
+ /**
310
+ * Running the AWS core rule set over an application's own traffic.
311
+ */
312
+
313
+ import { CreateWebACLCommand } from "@aws-sdk/client-wafv2";
314
+
315
+ import { SimAws } from "@kensio/yulin";
316
+
317
+ const waf = new SimAws().wafV2();
318
+
319
+ const visibility = {
320
+ SampledRequestsEnabled: false,
321
+ CloudWatchMetricsEnabled: false,
322
+ MetricName: "api",
323
+ };
324
+
325
+ const created = await waf.createWebAcl(
326
+ new CreateWebACLCommand({
327
+ Name: "api-acl",
328
+ Scope: "REGIONAL",
329
+ DefaultAction: { Allow: {} },
330
+ VisibilityConfig: visibility,
331
+ Rules: [
332
+ {
333
+ Name: "core-rule-set",
334
+ Priority: 0,
335
+ OverrideAction: { None: {} },
336
+ Statement: {
337
+ ManagedRuleGroupStatement: {
338
+ VendorName: "AWS",
339
+ Name: "AWSManagedRulesCommonRuleSet",
340
+ RuleActionOverrides: [
341
+ { Name: "NoUserAgent_HEADER", ActionToUse: { Count: {} } },
342
+ ],
343
+ },
344
+ },
345
+ VisibilityConfig: visibility,
346
+ },
347
+ ],
348
+ }),
349
+ );
350
+
351
+ const webAclArn = created.Summary!.ARN;
352
+
353
+ const healthCheck = waf.evaluateRequest({
354
+ webAclArn,
355
+ request: new Request("https://example.test/health"),
356
+ });
357
+ const traversal = waf.evaluateRequest({
358
+ webAclArn,
359
+ request: new Request(
360
+ "https://example.test/read?file=..%2F..%2Fetc%2Fpasswd",
361
+ {
362
+ headers: { "user-agent": "curl/8.5.0" },
363
+ },
364
+ ),
365
+ });
366
+
367
+ // "ALLOW" ["awswaf:managed:aws:core-rule-set:NoUserAgent_Header"]
368
+ console.log(healthCheck.action, healthCheck.labels);
369
+
370
+ // "BLOCK" "core-rule-set"
371
+ console.log(traversal.action, traversal.terminatingRuleName);
372
+ ```
373
+
374
+ The health check sends no User-Agent header, which `NoUserAgent_HEADER` claims. The override sets
375
+ that rule to `Count`, so the request goes through carrying the label.
376
+
377
+ `RuleActionOverrides`, `ScopeDownStatement` and `OverrideAction` behave as AWS documents them. An
378
+ `OverrideAction` of `Count` holds the whole group to counting whatever its rules were set to. A
379
+ `ScopeDownStatement` decides which requests the group sees at all, and a request it does not claim
380
+ picks up no label from the group.
381
+
382
+ `DescribeManagedRuleGroup` reports the rules of a group and the labels they add. An override names a
383
+ rule in the spelling that reports.
384
+
385
+ ## How closely the managed rules match
386
+
387
+ AWS publishes every rule name, every default action, every label and the size limits. It holds back
388
+ the pattern set behind each rule, and says so. Each rule here declares how closely it follows the
389
+ AWS rule it stands for, and `managedRules().rules()` reports the tier of every one.
390
+
391
+ - **exact** matches where the AWS rule matches. The four `SizeRestrictions_*` rules at their
392
+ documented limits (2,048 bytes for the query string, 10,240 for the cookie header, 8,192 for the
393
+ body and 1,024 for the URI path), along with `NoUserAgent_HEADER`, `PROPFIND_METHOD` and
394
+ `Host_localhost_HEADER`.
395
+ - **documented** matches the patterns AWS published and nothing beyond them. `Log4JRCE_*`,
396
+ `EC2MetaDataSSRF_*`, `GenericLFI_*`, `GenericRFI_*`, `RestrictedExtensions_*`,
397
+ `ExploitablePaths_URIPATH`, `AdminProtection_URIPATH`, `JavaDeserializationRCE_*` and
398
+ `UserAgent_BadBots_HEADER`.
399
+ - **declared** detects nothing at all. The four `CrossSiteScripting_*` rules run AWS's own
400
+ detection, and AWS documents none of it.
401
+
402
+ The tiers under-detect against AWS and never over-detect. The usual reason to put WAF in a test is
403
+ to find out whether an application's own traffic still gets through with the core rule set on. A
404
+ rule that blocked more than AWS blocks would fail that test for a request AWS allows, and send
405
+ somebody off to work around a rule that does not exist. A rule that blocks less is invisible to that
406
+ test and right on AWS too.
407
+
408
+ `AdminProtection_URIPATH` is the one to know about. AWS gives `sqlmanager` as its example pattern
409
+ and nothing else, so an application's own `/admin` paths reach it here. On AWS they may not.
410
+
411
+ The reverse test, asserting that an attack payload is blocked, is covered by declaring the match.
412
+ `onRequest` says which rules claim a request to one path, matched exactly.
413
+
414
+ ```typescript sim-wafv2-managed-declared-match
415
+ /**
416
+ * Declaring the cross-site scripting match AWS does not document.
417
+ */
418
+
419
+ import { SimAws } from "@kensio/yulin";
420
+
421
+ const waf = new SimAws().wafV2();
422
+
423
+ waf.managedRules().onRequest("/search", {
424
+ matches: ["CrossSiteScripting_QUERYARGUMENTS"],
425
+ });
426
+
427
+ // "declared"
428
+ console.log(waf.managedRules().tierOf("CrossSiteScripting_QUERYARGUMENTS"));
429
+
430
+ // "exact"
431
+ console.log(waf.managedRules().tierOf("SizeRestrictions_BODY"));
432
+ ```
433
+
434
+ A request to `/search` is then claimed by that rule, which labels it, blocks it and takes any
435
+ override written for it, as a rule that detected the payload itself would.
436
+
437
+ A match names the rule, in the spelling `RuleActionOverrides` and `DescribeManagedRuleGroup` use
438
+ (`CrossSiteScripting_QUERYARGUMENTS`), and not the label the rule adds
439
+ (`CrossSiteScripting_QueryArguments`). A name no simulated group holds is refused where it was
440
+ written.
441
+
442
+ Anything outside the three groups is refused by name, and the refusal says which are simulated. The
443
+ IP reputation and anonymous IP groups decide by caller address, and every request in this simulation
444
+ comes from one client. Bot Control and the account takeover groups decide by behaviour across
445
+ requests. The SQL injection group is undocumented in the way the cross-site scripting rules are.
446
+
447
+ ## Labels
448
+
449
+ A rule adds its labels to a request when it matches, and the rules that run after it can match on
450
+ them with a `LabelMatchStatement`. A `LABEL` scope matches one fully qualified label and a
451
+ `NAMESPACE` scope matches every label under a prefix.
452
+
453
+ This is how a managed rule group is tuned. Run the group in count mode, and block on the label of
454
+ the rule that matters.
455
+
456
+ ```typescript sim-wafv2-label-match
457
+ /**
458
+ * Blocking on a label the core rule set left behind.
459
+ */
460
+
461
+ import { CreateWebACLCommand } from "@aws-sdk/client-wafv2";
462
+
463
+ import { SimAws } from "@kensio/yulin";
464
+
465
+ const waf = new SimAws().wafV2();
466
+
467
+ const visibility = {
468
+ SampledRequestsEnabled: false,
469
+ CloudWatchMetricsEnabled: false,
470
+ MetricName: "api",
471
+ };
472
+
473
+ const created = await waf.createWebAcl(
474
+ new CreateWebACLCommand({
475
+ Name: "api-acl",
476
+ Scope: "REGIONAL",
477
+ DefaultAction: { Allow: {} },
478
+ VisibilityConfig: visibility,
479
+ Rules: [
480
+ {
481
+ Name: "core-rule-set",
482
+ Priority: 0,
483
+ OverrideAction: { Count: {} },
484
+ Statement: {
485
+ ManagedRuleGroupStatement: {
486
+ VendorName: "AWS",
487
+ Name: "AWSManagedRulesCommonRuleSet",
488
+ },
489
+ },
490
+ VisibilityConfig: visibility,
491
+ },
492
+ {
493
+ Name: "block-restricted-files",
494
+ Priority: 1,
495
+ Action: { Block: {} },
496
+ Statement: {
497
+ LabelMatchStatement: {
498
+ Scope: "LABEL",
499
+ Key: "awswaf:managed:aws:core-rule-set:RestrictedExtensions_URIPath",
500
+ },
501
+ },
502
+ VisibilityConfig: visibility,
503
+ },
504
+ ],
505
+ }),
506
+ );
507
+
508
+ const decision = waf.evaluateRequest({
509
+ webAclArn: created.Summary!.ARN,
510
+ request: new Request("https://example.test/app.ini", {
511
+ headers: { "user-agent": "curl/8.5.0" },
512
+ }),
513
+ });
514
+
515
+ // "BLOCK" "block-restricted-files"
516
+ console.log(decision.action, decision.terminatingRuleName);
517
+ ```
518
+
519
+ A rule of the web ACL's own adds a label under the name it gave it, with no prefix. A label from a
520
+ managed rule group is qualified by the group it came from. That is the
521
+ `awswaf:managed:aws:core-rule-set:` on the front of the key above.
522
+
523
+ ## Answering a blocked request
524
+
525
+ A `Block` action answers 403 with WAF's own body, and so does a request a protected REST API stage
526
+ blocked. Real API Gateway writes `{"message":"Forbidden"}` there. A `CustomResponse` overrides the
527
+ status and the
528
+ body, taking the body from the web ACL's `CustomResponseBodies` by key. It carries a `ResponseCode`
529
+ of its own, from 200 to 599, and any response headers it names reach the client under the names it
530
+ gave them.
531
+
532
+ The `x-amzn-waf-` prefix belongs to the other direction. WAF puts it on the headers an `Allow` or
533
+ `Count` action inserts into the request it forwards, which is what tells a rule's header apart from
534
+ one the client sent.
535
+
536
+ ```typescript sim-wafv2-custom-response
537
+ /**
538
+ * Answering a blocked request with a body the web ACL holds.
539
+ */
540
+
541
+ import { CreateWebACLCommand } from "@aws-sdk/client-wafv2";
542
+
543
+ import { SimAws } from "@kensio/yulin";
544
+ import { simWafBlockedHttpResponse } from "@kensio/yulin/wafv2";
545
+
546
+ const waf = new SimAws().wafV2();
547
+
548
+ const visibility = {
549
+ SampledRequestsEnabled: false,
550
+ CloudWatchMetricsEnabled: false,
551
+ MetricName: "api",
552
+ };
553
+
554
+ const created = await waf.createWebAcl(
555
+ new CreateWebACLCommand({
556
+ Name: "api-acl",
557
+ Scope: "REGIONAL",
558
+ DefaultAction: { Allow: {} },
559
+ VisibilityConfig: visibility,
560
+ CustomResponseBodies: {
561
+ "not-here": {
562
+ ContentType: "APPLICATION_JSON",
563
+ Content: '{"message":"Not found"}',
564
+ },
565
+ },
566
+ Rules: [
567
+ {
568
+ Name: "hide-admin",
569
+ Priority: 0,
570
+ Action: {
571
+ Block: {
572
+ CustomResponse: {
573
+ ResponseCode: 404,
574
+ CustomResponseBodyKey: "not-here",
575
+ ResponseHeaders: [{ Name: "rule", Value: "hide-admin" }],
576
+ },
577
+ },
578
+ },
579
+ Statement: {
580
+ ByteMatchStatement: {
581
+ FieldToMatch: { UriPath: {} },
582
+ PositionalConstraint: "STARTS_WITH",
583
+ SearchString: Buffer.from("/admin"),
584
+ TextTransformations: [{ Priority: 0, Type: "NONE" }],
585
+ },
586
+ },
587
+ VisibilityConfig: visibility,
588
+ },
589
+ ],
590
+ }),
591
+ );
592
+
593
+ const decision = waf.evaluateRequest({
594
+ webAclArn: created.Summary!.ARN,
595
+ request: new Request("https://example.test/admin"),
596
+ });
597
+ const response = simWafBlockedHttpResponse(decision.blocked!);
598
+
599
+ // 404 "hide-admin" '{"message":"Not found"}'
600
+ console.log(
601
+ response.status,
602
+ response.headers.get("rule"),
603
+ await response.text(),
604
+ );
605
+ ```
606
+
607
+ The default body is Yulin's own. Real WAF hands the blocking off to whatever the web ACL is in front
608
+ of, and each of those writes its own page. The status is 403 either way.
609
+
610
+ ## Protecting an API Gateway REST API stage
611
+
612
+ `AssociateWebACL` puts a `REGIONAL` web ACL in front of a simulated REST API stage, named by the
613
+ stage's ARN. `SimRestApi.stageArn` builds that ARN, of the form
614
+ `arn:aws:apigateway:<region>::/restapis/<api-id>/stages/<stage-name>`.
615
+
616
+ The stage then puts every request through the web ACL before it matches the method and before any
617
+ authorizer runs. That is the order real API Gateway evaluates in, ahead of resource policies, IAM,
618
+ Lambda authorizers and Cognito authorizers alike. A blocked request gets 403 with WAF's body, and
619
+ neither the authorizer nor the integration behind the method sees it. An allowed request carries on,
620
+ with the headers an `Allow` rule inserted added to what the integration receives.
621
+
622
+ ```typescript sim-wafv2-api-gateway-stage
623
+ /**
624
+ * Blocking a request to a REST API stage with a web ACL in front of it.
625
+ */
626
+
627
+ import {
628
+ AssociateWebACLCommand,
629
+ CreateWebACLCommand,
630
+ } from "@aws-sdk/client-wafv2";
631
+
632
+ import { SimAws } from "@kensio/yulin";
633
+ import { simRestApiLambdaProxyFactory } from "@kensio/yulin/apigateway";
634
+ import { serveSimAws } from "@kensio/yulin/serve";
635
+
636
+ const simAws = new SimAws();
637
+ const waf = simAws.wafV2();
638
+
639
+ const restApi = await simRestApiLambdaProxyFactory.make(
640
+ { handler: () => ({ statusCode: 200, body: "orders" }) },
641
+ simAws,
642
+ );
643
+
644
+ const visibility = {
645
+ SampledRequestsEnabled: false,
646
+ CloudWatchMetricsEnabled: false,
647
+ MetricName: "api",
648
+ };
649
+
650
+ const created = await waf.createWebAcl(
651
+ new CreateWebACLCommand({
652
+ Name: "api-acl",
653
+ Scope: "REGIONAL",
654
+ DefaultAction: { Allow: {} },
655
+ VisibilityConfig: visibility,
656
+ Rules: [
657
+ {
658
+ Name: "block-admin",
659
+ Priority: 0,
660
+ Action: { Block: {} },
661
+ Statement: {
662
+ ByteMatchStatement: {
663
+ FieldToMatch: { UriPath: {} },
664
+ PositionalConstraint: "CONTAINS",
665
+ SearchString: Buffer.from("/admin"),
666
+ TextTransformations: [{ Priority: 0, Type: "NONE" }],
667
+ },
668
+ },
669
+ VisibilityConfig: { ...visibility, MetricName: "block-admin" },
670
+ },
671
+ ],
672
+ }),
673
+ );
674
+
675
+ await waf.associateWebAcl(
676
+ new AssociateWebACLCommand({
677
+ WebACLArn: created.Summary?.ARN,
678
+ ResourceArn: restApi.stageArn("prod"),
679
+ }),
680
+ );
681
+
682
+ const srv = await serveSimAws({ simAws });
683
+
684
+ const blocked = await fetch(
685
+ srv.localUrl(`${restApi.invokeUrl("prod")}/admin/users`),
686
+ );
687
+ const allowed = await fetch(
688
+ srv.localUrl(`${restApi.invokeUrl("prod")}/orders`),
689
+ );
690
+
691
+ console.log(blocked.status, allowed.status);
692
+ // 403 200
693
+
694
+ await srv.close();
695
+ ```
696
+
697
+ `DisassociateWebACL` takes the web ACL back off. `GetWebACLForResource` reports the web ACL one
698
+ stage carries, and `ListResourcesForWebACL` reports the stages one web ACL protects. That listing
699
+ takes a `ResourceType` of `API_GATEWAY`. Real WAFv2 lists `APPLICATION_LOAD_BALANCER` for a request
700
+ that names no type. Load balancers are outside this simulation, and a listing that names no type is
701
+ refused.
702
+
703
+ Deleting the stage or the whole API takes the association with it. A stage deployed again under the
704
+ same name carries no web ACL. A web ACL that is still in front of a stage cannot be deleted, and
705
+ `DeleteWebACL` names the stages still pointing at it.
706
+
707
+ The web ACL and the stage belong to one Account and Region. A `CLOUDFRONT` scope web ACL is refused,
708
+ because a distribution takes its web ACL from the distribution and not from `AssociateWebACL`. A web
709
+ ACL from another Region or another Account is refused, as it is on AWS.
710
+
711
+ An API Gateway HTTP API stage is refused. AWS WAF has no resource type for one, and an association
712
+ accepted here would let a test cover protection AWS never applies. Application Load Balancer,
713
+ AppSync, App Runner, Amplify and Verified Access resources are refused as unsimulated, each naming
714
+ what it would have protected.
715
+
716
+ ## Protecting a Cognito user pool
717
+
718
+ `AssociateWebACL` puts a `REGIONAL` web ACL in front of a simulated user pool, named by the pool's
719
+ ARN. That ARN takes the form `arn:aws:cognito-idp:<region>:<account>:userpool/<pool-id>`, and
720
+ `SimCognitoIdentityProvider.userPool(id).arn.value` is where to read it from.
721
+
722
+ The pool's endpoints then go through the web ACL before the one a request named runs. Those are the
723
+ hosted domain (the authorize and token endpoints, `/logout`, and the managed login pages at
724
+ `/signup`, `/confirm`, `/forgotPassword` and `/confirmForgotPassword`) and the two documents the
725
+ pool publishes at `/<pool-id>/.well-known/jwks.json` and
726
+ `/<pool-id>/.well-known/openid-configuration`. The `/<pool-id>/messages` listing is Yulin's own and
727
+ sits outside the web ACL, as [below](#the-request-body-is-withheld-at-a-hosted-domain) says. A
728
+ blocked request gets 403 with WAF's body, whatever method it used. A blocked sign-up creates no user
729
+ and records no message.
730
+
731
+ The pages are usually the point. `/signup`, `/confirm` and `/forgotPassword` are the ones that
732
+ create an account or send an email, and a real web ACL on a user pool is usually written for them.
733
+
734
+ ```typescript sim-wafv2-cognito-user-pool
735
+ /**
736
+ * Blocking a request to a pool's hosted domain with a web ACL in front of it.
737
+ */
738
+
739
+ import {
740
+ CreateUserPoolClientCommand,
741
+ CreateUserPoolCommand,
742
+ CreateUserPoolDomainCommand,
743
+ } from "@aws-sdk/client-cognito-identity-provider";
744
+ import {
745
+ AssociateWebACLCommand,
746
+ CreateWebACLCommand,
747
+ } from "@aws-sdk/client-wafv2";
748
+
749
+ import { SimAws } from "@kensio/yulin";
750
+ import { serveSimAws } from "@kensio/yulin/serve";
751
+
752
+ const simAws = new SimAws({ defaultRegionName: "eu-west-2" });
753
+ const cognito = simAws.cognitoIdentityProvider();
754
+ const waf = simAws.wafV2();
755
+
756
+ const created = await cognito.createUserPool(
757
+ new CreateUserPoolCommand({ PoolName: "myapp-users" }),
758
+ );
759
+ const userPoolId = created.UserPool!.Id!;
760
+
761
+ await cognito.createUserPoolDomain(
762
+ new CreateUserPoolDomainCommand({
763
+ UserPoolId: userPoolId,
764
+ Domain: "myapp-login",
765
+ }),
766
+ );
767
+
768
+ const appClient = await cognito.createUserPoolClient(
769
+ new CreateUserPoolClientCommand({
770
+ UserPoolId: userPoolId,
771
+ ClientName: "web",
772
+ AllowedOAuthFlowsUserPoolClient: true,
773
+ AllowedOAuthFlows: ["code"],
774
+ AllowedOAuthScopes: ["openid"],
775
+ CallbackURLs: ["https://www.example.com/user/callback"],
776
+ SupportedIdentityProviders: ["COGNITO"],
777
+ }),
778
+ );
779
+
780
+ const visibility = {
781
+ SampledRequestsEnabled: false,
782
+ CloudWatchMetricsEnabled: false,
783
+ MetricName: "pool",
784
+ };
785
+
786
+ const webAcl = await waf.createWebAcl(
787
+ new CreateWebACLCommand({
788
+ Name: "pool-acl",
789
+ Scope: "REGIONAL",
790
+ DefaultAction: { Allow: {} },
791
+ VisibilityConfig: visibility,
792
+ Rules: [
793
+ {
794
+ Name: "block-scraper",
795
+ Priority: 0,
796
+ Action: { Block: {} },
797
+ Statement: {
798
+ ByteMatchStatement: {
799
+ FieldToMatch: { SingleHeader: { Name: "user-agent" } },
800
+ PositionalConstraint: "CONTAINS",
801
+ SearchString: Buffer.from("scraper"),
802
+ TextTransformations: [{ Priority: 0, Type: "NONE" }],
803
+ },
804
+ },
805
+ VisibilityConfig: { ...visibility, MetricName: "block-scraper" },
806
+ },
807
+ ],
808
+ }),
809
+ );
810
+
811
+ await waf.associateWebAcl(
812
+ new AssociateWebACLCommand({
813
+ WebACLArn: webAcl.Summary?.ARN,
814
+ ResourceArn: cognito.userPool(userPoolId).arn.value,
815
+ }),
816
+ );
817
+
818
+ const srv = await serveSimAws({ simAws });
819
+ const parameters = new URLSearchParams({
820
+ response_type: "code",
821
+ client_id: appClient.UserPoolClient!.ClientId!,
822
+ redirect_uri: "https://www.example.com/user/callback",
823
+ scope: "openid",
824
+ });
825
+ const signInUrl = srv.localUrl(
826
+ `https://myapp-login.auth.eu-west-2.amazoncognito.com/oauth2/authorize?${parameters.toString()}`,
827
+ );
828
+
829
+ const blocked = await fetch(signInUrl, {
830
+ headers: { "user-agent": "scraper/1.0" },
831
+ });
832
+ const allowed = await fetch(signInUrl);
833
+
834
+ console.log(blocked.status, allowed.status);
835
+ // 403 200
836
+
837
+ await srv.close();
838
+ ```
839
+
840
+ `DisassociateWebACL` takes the web ACL back off. `GetWebACLForResource` reports the web ACL one pool
841
+ carries, and `ListResourcesForWebACL` reports the pools one web ACL protects under a `ResourceType`
842
+ of `COGNITO_USER_POOL`. Deleting the pool takes the association with it, and a web ACL still in
843
+ front of a pool cannot be deleted.
844
+
845
+ The web ACL and the pool belong to one Account and Region. A `CLOUDFRONT` scope web ACL is refused,
846
+ because a distribution takes its web ACL from the distribution. A pool in another Account or another
847
+ Region is refused as well.
848
+
849
+ AWS also refuses a web ACL carrying `AWSManagedRulesATPRuleSet`, and it refuses the whole web ACL
850
+ over the one rule group. Yulin turns that group away earlier, at `CreateWebACL`, along with every
851
+ managed rule group outside the [three that are simulated](#the-aws-managed-rule-groups).
852
+
853
+ ### The request body is withheld at a hosted domain
854
+
855
+ Cognito sends AWS WAF the headers and the path of a managed login request and none of its body. A
856
+ `ByteMatchStatement`, `RegexMatchStatement` or `SizeConstraintStatement` on `Body` therefore
857
+ inspects an empty field at a hosted domain, however well formed the rule is. Keying a rule on a
858
+ username or a password is out for the same reason. Yulin withholds the body the same way. A rule
859
+ written against it fails here as it fails on AWS.
860
+
861
+ Real WAF does read the body of a user pool API request such as `SignUp` or `InitiateAuth`. Those
862
+ reach Yulin as SDK Commands and carry no HTTP request for a rule to read. No web ACL is evaluated
863
+ for them at all. A test covering an API operation should reach for
864
+ [`evaluateRequest`](#deciding-what-happens-to-a-request) with a request of its own.
865
+
866
+ Two paths are outside what the web ACL sees. `/<pool-id>/messages` is Yulin's own listing of the
867
+ messages a pool would have sent, and real Cognito has no such endpoint. Managed login branding and
868
+ its assets are outside the simulation.
869
+
870
+ ## Protecting a CloudFront distribution
871
+
872
+ A simulated CloudFront distribution names its web ACL in `WebACLId` on its `DistributionConfig`,
873
+ and evaluates it against every request that reaches the distribution. A blocked request gets 403
874
+ before a cache behaviour, a viewer-request CloudFront Function or the origin sees it.
875
+
876
+ CloudFront is associated this way and not through `AssociateWebACL`, which real WAF keeps for the
877
+ regional resource types. The ARN has to name a `CLOUDFRONT` [scope](#scopes) web ACL. See
878
+ [web ACLs in the CloudFront docs](https://yulinsim.dev/services/cloudfront/#web-acls) for the whole example.
879
+
880
+ CloudFront has no association Resource, so in a template the reference is a property of the
881
+ distribution itself. A `WebACLId` naming a web ACL from outside this simulation is left out and
882
+ recorded on `stack.ignoredProperties`, and the distribution deploys and serves every request. The
883
+ alternative would take a whole site down over a firewall, which is a worse answer than serving the
884
+ site unprotected and saying so.
885
+
886
+ ## Regex pattern sets
887
+
888
+ A rule can point at a regex pattern set by ARN, and matches when any expression in the set matches.
889
+ The set is resolved when the rule is written. An ARN naming nothing is refused by `CreateWebACL`
890
+ the way real WAF refuses it.
891
+
892
+ ```typescript sim-wafv2-regex-pattern-set
893
+ /**
894
+ * Blocking a set of user agents held in a regex pattern set.
895
+ */
896
+
897
+ import {
898
+ CreateRegexPatternSetCommand,
899
+ CreateWebACLCommand,
900
+ } from "@aws-sdk/client-wafv2";
901
+
902
+ import { SimAws } from "@kensio/yulin";
903
+
904
+ const waf = new SimAws().wafV2();
905
+
906
+ const patternSet = await waf.createRegexPatternSet(
907
+ new CreateRegexPatternSetCommand({
908
+ Name: "scanners",
909
+ Scope: "REGIONAL",
910
+ RegularExpressionList: [
911
+ { RegexString: "sqlmap" },
912
+ { RegexString: "nikto" },
913
+ ],
914
+ }),
915
+ );
916
+
917
+ const visibility = {
918
+ SampledRequestsEnabled: false,
919
+ CloudWatchMetricsEnabled: false,
920
+ MetricName: "api",
921
+ };
922
+
923
+ const created = await waf.createWebAcl(
924
+ new CreateWebACLCommand({
925
+ Name: "api-acl",
926
+ Scope: "REGIONAL",
927
+ DefaultAction: { Allow: {} },
928
+ VisibilityConfig: visibility,
929
+ Rules: [
930
+ {
931
+ Name: "block-scanners",
932
+ Priority: 0,
933
+ Action: { Block: {} },
934
+ Statement: {
935
+ RegexPatternSetReferenceStatement: {
936
+ ARN: patternSet.Summary!.ARN,
937
+ FieldToMatch: { SingleHeader: { Name: "user-agent" } },
938
+ TextTransformations: [{ Priority: 0, Type: "LOWERCASE" }],
939
+ },
940
+ },
941
+ VisibilityConfig: visibility,
942
+ },
943
+ ],
944
+ }),
945
+ );
946
+
947
+ const decision = waf.evaluateRequest({
948
+ webAclArn: created.Summary!.ARN,
949
+ request: new Request("https://example.test/", {
950
+ headers: { "user-agent": "sqlmap/1.7" },
951
+ }),
952
+ });
953
+
954
+ // "BLOCK"
955
+ console.log(decision.action);
956
+ ```
957
+
958
+ An update to a pattern set reaches the rules pointing at it. A reference resolves to the set when
959
+ the rule is written and reads its expressions when a request arrives, as it does on AWS.
960
+
961
+ IP sets are created, read, updated, listed and deleted the same way. No rule reads one, for the
962
+ reason in [Refusals](#refusals) below.
963
+
964
+ ## Deploying web ACLs with CloudFormation
965
+
966
+ `AWS::WAFv2::WebACL`, `AWS::WAFv2::WebACLAssociation`, `AWS::WAFv2::IPSet` and
967
+ `AWS::WAFv2::RegexPatternSet` deploy into simulated WAFv2. CDK ships no L2 construct for WAFv2. A
968
+ project protecting an API writes `CfnWebACL` and `CfnWebACLAssociation` by hand, and the template
969
+ those synthesize to is the one that deploys here.
970
+
971
+ ```typescript sim-wafv2-cloudformation
972
+ /**
973
+ * Deploying a web ACL from a CloudFormation template.
974
+ */
975
+
976
+ import { SimAws } from "@kensio/yulin";
977
+
978
+ const simAws = new SimAws();
979
+
980
+ const visibility = {
981
+ SampledRequestsEnabled: false,
982
+ CloudWatchMetricsEnabled: false,
983
+ MetricName: "orders",
984
+ };
985
+
986
+ const stack = await simAws.cloudFormation().deployTemplate({
987
+ stackName: "orders",
988
+ template: {
989
+ Resources: {
990
+ OrdersAcl: {
991
+ Type: "AWS::WAFv2::WebACL",
992
+ Properties: {
993
+ Name: "orders-acl",
994
+ Scope: "REGIONAL",
995
+ DefaultAction: { Allow: {} },
996
+ VisibilityConfig: visibility,
997
+ Rules: [
998
+ {
999
+ Name: "block-admin",
1000
+ Priority: 0,
1001
+ Action: { Block: {} },
1002
+ Statement: {
1003
+ ByteMatchStatement: {
1004
+ FieldToMatch: { UriPath: {} },
1005
+ PositionalConstraint: "CONTAINS",
1006
+ SearchString: "/admin",
1007
+ TextTransformations: [{ Priority: 0, Type: "NONE" }],
1008
+ },
1009
+ },
1010
+ VisibilityConfig: { ...visibility, MetricName: "block-admin" },
1011
+ },
1012
+ ],
1013
+ },
1014
+ },
1015
+ },
1016
+ Outputs: { AclArn: { Value: { "Fn::GetAtt": ["OrdersAcl", "Arn"] } } },
1017
+ },
1018
+ });
1019
+
1020
+ const decision = simAws.wafV2().evaluateRequest({
1021
+ webAclArn: stack.outputs.get("AclArn")!.value as string,
1022
+ request: new Request("https://orders.example.test/admin/users"),
1023
+ });
1024
+
1025
+ // "BLOCK"
1026
+ console.log(decision.action);
1027
+ ```
1028
+
1029
+ A template spells a web ACL the way the API spells it, with two exceptions. A `SearchString` is
1030
+ plain text in a template where the SDK takes bytes, and a `RegularExpressionList` is a list of
1031
+ strings where the SDK takes a list of `RegexString` objects. Both are read here the way
1032
+ CloudFormation writes them.
1033
+
1034
+ Every rule is compiled while the stack deploys. A rule this simulator will not evaluate (see
1035
+ [Refusals](#refusals)) is left out of the web ACL, and the web ACL deploys with the rules that are
1036
+ left. The rule that went missing is recorded on `stack.ignoredProperties`, under the logical ID that
1037
+ declared it, and the reason is the one `CreateWebACL` gives an SDK caller.
1038
+
1039
+ ```typescript
1040
+ const [dropped] = stack.ignoredProperties;
1041
+
1042
+ // "OrdersAcl Rules.block-countries"
1043
+ console.log(`${dropped.logicalId} ${dropped.path}`);
1044
+
1045
+ // "Rule block-countries uses the statement kind GeoMatchStatement, which
1046
+ // Yulin does not simulate: ..."
1047
+ console.log(dropped.reason);
1048
+ ```
1049
+
1050
+ The web ACL is then real, and thinner than the one the template describes. Requests the dropped rule
1051
+ would have blocked are served by whatever the web ACL is in front of. That is the size of what a
1052
+ test loses, and `stack.ignoredProperties` is where to read it. An SDK caller writing the same rule
1053
+ is refused outright, because a request that was answered and then quietly emptied is a worse answer
1054
+ than a refusal.
1055
+
1056
+ The same goes for a web ACL member with no behaviour behind it, such as `CaptchaConfig`. The web ACL
1057
+ deploys without it and the member is recorded.
1058
+
1059
+ A web ACL nothing coherent could be deployed from still fails the stack. A `Scope` outside
1060
+ `REGIONAL` and `CLOUDFRONT`, a `Rules` written as an object, a `Name` written as a number. The
1061
+ failure names the logical ID.
1062
+
1063
+ `Name` is optional on all three named types. An unnamed resource is named after the stack, the
1064
+ logical ID and a tail derived from both, as real CloudFormation names one. The web ACL above sets a
1065
+ `Name` and keeps `orders-acl`. With that property left out it would have deployed as
1066
+ `orders-OrdersAcl-5615bd3c857f`, and [the CloudFormation docs](https://yulinsim.dev/services/cloudformation/#names-cloudformation-generates "Names CloudFormation generates")
1067
+ cover where the tail comes from.
1068
+
1069
+ ### Putting a deployed web ACL in front of something
1070
+
1071
+ `AWS::WAFv2::WebACLAssociation` associates a web ACL with whatever its `ResourceArn` names, which
1072
+ covers an API Gateway REST API stage and a Cognito user pool. It goes through `AssociateWebACL` and
1073
+ inherits that command's answers. An ARN naming an HTTP API stage fails the deployment, because AWS
1074
+ WAF protects no HTTP API and neither does real CloudFormation. An ARN naming a load balancer or an
1075
+ AppSync API skips the association. AWS WAF protects both, and Yulin simulates a web ACL in front of
1076
+ neither.
1077
+
1078
+ An association naming a web ACL from outside this simulation is skipped too, which covers a template
1079
+ naming one from a real account and one whose web ACL is in another Region. The stage or the pool
1080
+ deploys and serves, unprotected, and the association is the only Resource that goes missing.
1081
+ `skippedReason` names the ARN.
1082
+
1083
+ ```json
1084
+ {
1085
+ "OrdersAclAssociation": {
1086
+ "Type": "AWS::WAFv2::WebACLAssociation",
1087
+ "Properties": {
1088
+ "ResourceArn": {
1089
+ "Fn::Join": [
1090
+ "",
1091
+ [
1092
+ "arn:aws:apigateway:",
1093
+ { "Ref": "AWS::Region" },
1094
+ "::/restapis/",
1095
+ { "Ref": "Api" },
1096
+ "/stages/",
1097
+ { "Ref": "Stage" }
1098
+ ]
1099
+ ]
1100
+ },
1101
+ "WebACLArn": { "Fn::GetAtt": ["OrdersAcl", "Arn"] }
1102
+ }
1103
+ }
1104
+ }
1105
+ ```
1106
+
1107
+ That `Fn::Join` is what CDK's `api.deploymentStage.stageArn` synthesizes to. Deleting the
1108
+ association disassociates, and deleting the stack takes the association down before the web ACL it
1109
+ names.
1110
+
1111
+ A CloudFront distribution is associated through the distribution. `WebACLId` on
1112
+ `AWS::CloudFront::Distribution` holds a `CLOUDFRONT` [scope](#scopes) web ACL's ARN, usually as an
1113
+ `Fn::GetAtt` on a `CfnWebACL` in the same template. See
1114
+ [Protecting a CloudFront distribution](#protecting-a-cloudfront-distribution).
1115
+
1116
+ ### Attributes and Ref
1117
+
1118
+ `Fn::GetAtt` on a web ACL answers `Arn`, `Id`, `Capacity` and `LabelNamespace`. `Arn` is the one a
1119
+ template usually wants, since an association and a distribution both name a web ACL by ARN. The two
1120
+ sets answer `Arn` and `Id`.
1121
+
1122
+ `Capacity` adds up what AWS publishes for each rule. A byte match costs 2 or 10 depending on the
1123
+ match it makes, a regex match 3, a pattern set reference 25, a size constraint 1 and a label match
1124
+ 1, with 10 more for reading every query argument and 10 for each text transformation other than
1125
+ `NONE`. A managed rule group costs the fixed capacity its owner gave it.
1126
+
1127
+ The sum is an upper bound on the number AWS reports. Real WAF charges a web ACL the sum of its rules
1128
+ minus whatever work it can share between them, and publishes no description of when it shares any.
1129
+ Nothing here enforces the 5,000 unit maximum on a web ACL or the 1,500 units the base price covers.
1130
+ `GetWebACL` reports the same number.
1131
+
1132
+ `Ref` answers the physical ID, which WAFv2 spells in three parts (`orders-acl|<id>|REGIONAL`). It
1133
+ reads oddly beside every other service, and it is what AWS answers. WAFv2 resources carry a
1134
+ composite primary identifier of name, ID and scope. An association's physical ID is the resource ARN
1135
+ and the web ACL ARN joined by a pipe, and it publishes no attributes.
1136
+
1137
+ `AWS::WAFv2::RuleGroup` and `AWS::WAFv2::LoggingConfiguration` are recorded as unsupported and
1138
+ stepped over. A rule naming a rule group is refused anyway, and there is no log here to write to.
1139
+
1140
+ ## Scopes
1141
+
1142
+ A web ACL is created in `CLOUDFRONT` or `REGIONAL` scope. The two are separate namespaces, and one
1143
+ name can be taken in both.
1144
+
1145
+ `CLOUDFRONT` scope resources live in `us-east-1`, because CloudFront is global. A `CLOUDFRONT`
1146
+ request made anywhere else is refused, as real WAFv2 refuses it.
1147
+
1148
+ ```typescript sim-wafv2-cloudfront-scope
1149
+ /**
1150
+ * Creating a web ACL for a CloudFront distribution.
1151
+ */
1152
+
1153
+ import { CreateWebACLCommand } from "@aws-sdk/client-wafv2";
1154
+
1155
+ import { SimAws } from "@kensio/yulin";
1156
+
1157
+ const simAws = new SimAws();
1158
+ const waf = simAws
1159
+ .accountRegionScope(simAws.defaultAccountId, "us-east-1")
1160
+ .wafV2();
1161
+
1162
+ const created = await waf.createWebAcl(
1163
+ new CreateWebACLCommand({
1164
+ Name: "site-acl",
1165
+ Scope: "CLOUDFRONT",
1166
+ DefaultAction: { Allow: {} },
1167
+ VisibilityConfig: {
1168
+ SampledRequestsEnabled: false,
1169
+ CloudWatchMetricsEnabled: false,
1170
+ MetricName: "site",
1171
+ },
1172
+ }),
1173
+ );
1174
+
1175
+ // arn:aws:wafv2:us-east-1:...:global/webacl/site-acl/...
1176
+ console.log(created.Summary?.ARN);
1177
+ ```
1178
+
1179
+ ## Lock tokens
1180
+
1181
+ Every WAFv2 resource carries a lock token that changes on each write. The updates and the deletes
1182
+ take the token from the last read, and a write made against a stale one is refused with
1183
+ `WAFOptimisticLockException`.
1184
+
1185
+ `CreateWebACL` reports the first token in its summary, and `GetWebACL` reports the current one.
1186
+ `UpdateWebACL` answers with `NextLockToken` for the write after it.
1187
+
1188
+ ## Descriptions
1189
+
1190
+ A web ACL, an IP set and a regex pattern set each take an optional `Description`. WAFv2 holds it to
1191
+ between 1 and 256 characters, and to a pattern of word characters, spaces and `+=:#@/-,.` with
1192
+ neither end a space. The shortest description it matches is three characters long.
1193
+
1194
+ Both checks run on `CreateWebACL`, `UpdateWebACL`, `CreateIPSet`, `UpdateIPSet`,
1195
+ `CreateRegexPatternSet` and `UpdateRegexPatternSet`. A description outside either one is refused
1196
+ with `ValidationException`, naming every constraint it failed the way AWS names them. A write that
1197
+ leaves `Description` out is taken, and the resource keeps no description.
1198
+
1199
+ The empty string is the case worth knowing about. Code that reads a web ACL, replaces the rules and
1200
+ writes every other field back hands the description straight through, because `UpdateWebACL` clears
1201
+ whatever a write leaves out. AWS answers `Description: ""` for some web ACLs nobody has described,
1202
+ and refuses the write that gives it back:
1203
+
1204
+ ```text
1205
+ ValidationException: 2 validation errors detected:
1206
+ Value '' at 'description' failed to satisfy constraint: Member must have length greater than or equal to 1;
1207
+ Value '' at 'description' failed to satisfy constraint: Member must satisfy regular expression pattern: ^[\w+=:#@/\-,\.][\w+=:#@/\-,\.\s]+[\w+=:#@/\-,\.]$
1208
+ ```
1209
+
1210
+ Yulin leaves the field undefined for a web ACL nobody has described, and that difference stands. It
1211
+ is observed from one ACL and AWS documents nothing about it.
1212
+
1213
+ ## Permissions
1214
+
1215
+ Every command goes through simulated IAM. An operation on one resource authorizes against that
1216
+ resource's ARN. The id in the ARN is generated. A policy that names a resource therefore ends in a
1217
+ wildcard where the id goes.
1218
+
1219
+ `ListWebACLs`, `ListIPSets` and `ListRegexPatternSets` have no resource type on real WAFv2. They
1220
+ authorize against `*`, and a policy scoped to web ACL ARNs allows none of them, however broadly
1221
+ those ARNs are written.
1222
+
1223
+ ```typescript sim-wafv2-permissions
1224
+ /**
1225
+ * Reading a web ACL as a Role, with a policy naming it.
1226
+ */
1227
+
1228
+ import { CreateRoleCommand, PutRolePolicyCommand } from "@aws-sdk/client-iam";
1229
+ import { CreateWebACLCommand, GetWebACLCommand } from "@aws-sdk/client-wafv2";
1230
+
1231
+ import { SimAws } from "@kensio/yulin";
1232
+
1233
+ const simAws = new SimAws({ defaultAccountId: "111111111111" });
1234
+ const roleArn = "arn:aws:iam::111111111111:role/FirewallReaderRole";
1235
+
1236
+ await simAws.iam().createRole(
1237
+ new CreateRoleCommand({
1238
+ RoleName: "FirewallReaderRole",
1239
+ AssumeRolePolicyDocument: JSON.stringify({
1240
+ Version: "2012-10-17",
1241
+ Statement: [
1242
+ {
1243
+ Effect: "Allow",
1244
+ Principal: { Service: "lambda.amazonaws.com" },
1245
+ Action: "sts:AssumeRole",
1246
+ },
1247
+ ],
1248
+ }),
1249
+ }),
1250
+ );
1251
+
1252
+ await simAws.iam().putRolePolicy(
1253
+ new PutRolePolicyCommand({
1254
+ RoleName: "FirewallReaderRole",
1255
+ PolicyName: "ReadApiAcl",
1256
+ PolicyDocument: JSON.stringify({
1257
+ Version: "2012-10-17",
1258
+ Statement: [
1259
+ {
1260
+ Effect: "Allow",
1261
+ Action: "wafv2:GetWebACL",
1262
+ Resource:
1263
+ "arn:aws:wafv2:us-east-1:111111111111:regional/webacl/api-acl/*",
1264
+ },
1265
+ ],
1266
+ }),
1267
+ }),
1268
+ );
1269
+
1270
+ const waf = simAws.wafV2();
1271
+ const created = await waf.createWebAcl(
1272
+ new CreateWebACLCommand({
1273
+ Name: "api-acl",
1274
+ Scope: "REGIONAL",
1275
+ DefaultAction: { Allow: {} },
1276
+ VisibilityConfig: {
1277
+ SampledRequestsEnabled: false,
1278
+ CloudWatchMetricsEnabled: false,
1279
+ MetricName: "api",
1280
+ },
1281
+ }),
1282
+ );
1283
+
1284
+ const read = await waf.getWebAcl(
1285
+ new GetWebACLCommand({
1286
+ Name: "api-acl",
1287
+ Scope: "REGIONAL",
1288
+ Id: created.Summary?.Id,
1289
+ }),
1290
+ { caller: { kind: "arn", arn: roleArn } },
1291
+ );
1292
+
1293
+ // "api-acl"
1294
+ console.log(read.WebACL?.Name);
1295
+ ```
1296
+
1297
+ ## SDK interception
1298
+
1299
+ An intercepted `WAFV2Client` routes to the simulated WAFv2 in the Account and Region the client was
1300
+ configured for. See [the SDK docs](https://yulinsim.dev/sdk/) for how interception works.
1301
+
1302
+ ```typescript sim-wafv2-sdk-interception
1303
+ /**
1304
+ * Routing an intercepted WAFv2 SDK client to the simulator.
1305
+ */
1306
+
1307
+ import { CreateWebACLCommand, WAFV2Client } from "@aws-sdk/client-wafv2";
1308
+
1309
+ import { SimSdk } from "@kensio/yulin/sdk";
1310
+
1311
+ using simSdk = new SimSdk();
1312
+
1313
+ simSdk.intercept(WAFV2Client);
1314
+
1315
+ const client = new WAFV2Client({ region: "eu-west-2" });
1316
+
1317
+ await client.send(
1318
+ new CreateWebACLCommand({
1319
+ Name: "api-acl",
1320
+ Scope: "REGIONAL",
1321
+ DefaultAction: { Allow: {} },
1322
+ VisibilityConfig: {
1323
+ SampledRequestsEnabled: false,
1324
+ CloudWatchMetricsEnabled: false,
1325
+ MetricName: "api",
1326
+ },
1327
+ }),
1328
+ );
1329
+
1330
+ const scoped = simSdk.simAws.accountRegionScope(
1331
+ simSdk.simAws.defaultAccountId,
1332
+ "eu-west-2",
1333
+ );
1334
+
1335
+ // "api-acl"
1336
+ console.log(scoped.wafV2().allWebAcls("REGIONAL")[0]?.name);
1337
+ ```
1338
+
1339
+ ## Refusals
1340
+
1341
+ A rule Yulin cannot evaluate is refused by `CreateWebACL` and `UpdateWebACL`, naming the rule and
1342
+ what in it was refused. A web ACL that accepted such a rule would allow a request AWS blocks, and a
1343
+ silent hole in a security layer is worse than a missing one.
1344
+
1345
+ A template carrying one of these keeps the caution and drops the blast radius. The rule is left out,
1346
+ the web ACL deploys with the rest of them, and the omission is recorded. See
1347
+ [Deploying web ACLs with CloudFormation](#deploying-web-acls-with-cloudformation).
1348
+
1349
+ These statement kinds are refused:
1350
+
1351
+ - `IPSetReferenceStatement`, `GeoMatchStatement` and `AsnMatchStatement`. Every request in this
1352
+ simulation reports a source address of `127.0.0.1`, and a rule on where a request came from would
1353
+ see one client for the whole simulation.
1354
+ - `SqliMatchStatement` and `XssMatchStatement`. AWS publishes no description of the detection they
1355
+ run.
1356
+ - `RuleGroupReferenceStatement`. A rule group of your own is a resource in its own right, and none
1357
+ is simulated. The three simulated AWS managed rule groups are named in a statement rather than
1358
+ created.
1359
+
1360
+ A `RateBasedStatement` is evaluated (see [Rate limiting](#rate-limiting)). Two of its aggregation
1361
+ key types are refused. `FORWARDED_IP` and `ForwardedIPConfig` read the address from a forwarding
1362
+ header, which needs the source address variety an IP set is waiting on. `CUSTOM_KEYS` and
1363
+ `CustomKeys` aggregate on headers, cookies and query arguments, and are feasible and not part of
1364
+ this yet. `GetRateBasedStatementManagedKeys` is not simulated.
1365
+
1366
+ `JsonBody`, `HeaderOrder`, `UriFragment`, `JA3Fingerprint` and `JA4Fingerprint` are refused as
1367
+ fields to match. The `Captcha` and `Challenge` actions are refused, along with the `CaptchaConfig`,
1368
+ `ChallengeConfig` and `TokenDomains` that configure them, because a browser has to answer them.
1369
+
1370
+ Tags, logging, sampled requests and CloudWatch metrics for a web ACL are not simulated.
1371
+ `AssociationConfig`, `DataProtectionConfig`, `OnSourceDDoSProtectionConfig` and `ApplicationConfig`
1372
+ are refused for the same reason, each naming what it would have configured.
1373
+
1374
+ ## Simulated commands
1375
+
1376
+ `CreateWebACL`, `GetWebACL`, `UpdateWebACL`, `ListWebACLs`, `DeleteWebACL`, `CreateIPSet`,
1377
+ `GetIPSet`, `UpdateIPSet`, `ListIPSets`, `DeleteIPSet`, `CreateRegexPatternSet`,
1378
+ `GetRegexPatternSet`, `UpdateRegexPatternSet`, `ListRegexPatternSets`, `DeleteRegexPatternSet`,
1379
+ `DescribeManagedRuleGroup`, `AssociateWebACL`, `DisassociateWebACL`, `GetWebACLForResource` and
1380
+ `ListResourcesForWebACL`.