@composurecdk/wafv2 0.0.0-stage → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +240 -2
  3. package/dist/commonjs/alarm-config.d.ts +60 -0
  4. package/dist/commonjs/alarm-config.d.ts.map +1 -0
  5. package/dist/commonjs/alarm-config.js +3 -0
  6. package/dist/commonjs/alarm-config.js.map +1 -0
  7. package/dist/commonjs/alarm-defaults.d.ts +34 -0
  8. package/dist/commonjs/alarm-defaults.d.ts.map +1 -0
  9. package/dist/commonjs/alarm-defaults.js +37 -0
  10. package/dist/commonjs/alarm-defaults.js.map +1 -0
  11. package/dist/commonjs/alarms.d.ts +18 -0
  12. package/dist/commonjs/alarms.d.ts.map +1 -0
  13. package/dist/commonjs/alarms.js +94 -0
  14. package/dist/commonjs/alarms.js.map +1 -0
  15. package/dist/commonjs/association.d.ts +21 -0
  16. package/dist/commonjs/association.d.ts.map +1 -0
  17. package/dist/commonjs/association.js +61 -0
  18. package/dist/commonjs/association.js.map +1 -0
  19. package/dist/commonjs/custom-response.d.ts +52 -0
  20. package/dist/commonjs/custom-response.d.ts.map +1 -0
  21. package/dist/commonjs/custom-response.js +137 -0
  22. package/dist/commonjs/custom-response.js.map +1 -0
  23. package/dist/commonjs/defaults.d.ts +89 -0
  24. package/dist/commonjs/defaults.d.ts.map +1 -0
  25. package/dist/commonjs/defaults.js +90 -0
  26. package/dist/commonjs/defaults.js.map +1 -0
  27. package/dist/commonjs/index.d.ts +30 -0
  28. package/dist/commonjs/index.d.ts.map +1 -0
  29. package/dist/commonjs/index.js +35 -0
  30. package/dist/commonjs/index.js.map +1 -0
  31. package/dist/commonjs/logging-filter.d.ts +27 -0
  32. package/dist/commonjs/logging-filter.d.ts.map +1 -0
  33. package/dist/commonjs/logging-filter.js +17 -0
  34. package/dist/commonjs/logging-filter.js.map +1 -0
  35. package/dist/commonjs/logging.d.ts +59 -0
  36. package/dist/commonjs/logging.d.ts.map +1 -0
  37. package/dist/commonjs/logging.js +91 -0
  38. package/dist/commonjs/logging.js.map +1 -0
  39. package/dist/commonjs/metrics.d.ts +58 -0
  40. package/dist/commonjs/metrics.d.ts.map +1 -0
  41. package/dist/commonjs/metrics.js +67 -0
  42. package/dist/commonjs/metrics.js.map +1 -0
  43. package/dist/commonjs/names.d.ts +46 -0
  44. package/dist/commonjs/names.d.ts.map +1 -0
  45. package/dist/commonjs/names.js +140 -0
  46. package/dist/commonjs/names.js.map +1 -0
  47. package/dist/commonjs/package.json +3 -0
  48. package/dist/commonjs/rule-builder.d.ts +123 -0
  49. package/dist/commonjs/rule-builder.d.ts.map +1 -0
  50. package/dist/commonjs/rule-builder.js +163 -0
  51. package/dist/commonjs/rule-builder.js.map +1 -0
  52. package/dist/commonjs/scope.d.ts +15 -0
  53. package/dist/commonjs/scope.d.ts.map +1 -0
  54. package/dist/commonjs/scope.js +35 -0
  55. package/dist/commonjs/scope.js.map +1 -0
  56. package/dist/commonjs/web-acl-builder.d.ts +152 -0
  57. package/dist/commonjs/web-acl-builder.d.ts.map +1 -0
  58. package/dist/commonjs/web-acl-builder.js +205 -0
  59. package/dist/commonjs/web-acl-builder.js.map +1 -0
  60. package/dist/esm/alarm-config.d.ts +60 -0
  61. package/dist/esm/alarm-config.d.ts.map +1 -0
  62. package/dist/esm/alarm-config.js +2 -0
  63. package/dist/esm/alarm-config.js.map +1 -0
  64. package/dist/esm/alarm-defaults.d.ts +34 -0
  65. package/dist/esm/alarm-defaults.d.ts.map +1 -0
  66. package/dist/esm/alarm-defaults.js +34 -0
  67. package/dist/esm/alarm-defaults.js.map +1 -0
  68. package/dist/esm/alarms.d.ts +18 -0
  69. package/dist/esm/alarms.d.ts.map +1 -0
  70. package/dist/esm/alarms.js +90 -0
  71. package/dist/esm/alarms.js.map +1 -0
  72. package/dist/esm/association.d.ts +21 -0
  73. package/dist/esm/association.d.ts.map +1 -0
  74. package/dist/esm/association.js +58 -0
  75. package/dist/esm/association.js.map +1 -0
  76. package/dist/esm/custom-response.d.ts +52 -0
  77. package/dist/esm/custom-response.d.ts.map +1 -0
  78. package/dist/esm/custom-response.js +133 -0
  79. package/dist/esm/custom-response.js.map +1 -0
  80. package/dist/esm/defaults.d.ts +89 -0
  81. package/dist/esm/defaults.d.ts.map +1 -0
  82. package/dist/esm/defaults.js +87 -0
  83. package/dist/esm/defaults.js.map +1 -0
  84. package/dist/esm/index.d.ts +30 -0
  85. package/dist/esm/index.d.ts.map +1 -0
  86. package/dist/esm/index.js +23 -0
  87. package/dist/esm/index.js.map +1 -0
  88. package/dist/esm/logging-filter.d.ts +27 -0
  89. package/dist/esm/logging-filter.d.ts.map +1 -0
  90. package/dist/esm/logging-filter.js +14 -0
  91. package/dist/esm/logging-filter.js.map +1 -0
  92. package/dist/esm/logging.d.ts +59 -0
  93. package/dist/esm/logging.d.ts.map +1 -0
  94. package/dist/esm/logging.js +88 -0
  95. package/dist/esm/logging.js.map +1 -0
  96. package/dist/esm/metrics.d.ts +58 -0
  97. package/dist/esm/metrics.d.ts.map +1 -0
  98. package/dist/esm/metrics.js +63 -0
  99. package/dist/esm/metrics.js.map +1 -0
  100. package/dist/esm/names.d.ts +46 -0
  101. package/dist/esm/names.d.ts.map +1 -0
  102. package/dist/esm/names.js +132 -0
  103. package/dist/esm/names.js.map +1 -0
  104. package/dist/esm/package.json +3 -0
  105. package/dist/esm/rule-builder.d.ts +123 -0
  106. package/dist/esm/rule-builder.d.ts.map +1 -0
  107. package/dist/esm/rule-builder.js +160 -0
  108. package/dist/esm/rule-builder.js.map +1 -0
  109. package/dist/esm/scope.d.ts +15 -0
  110. package/dist/esm/scope.d.ts.map +1 -0
  111. package/dist/esm/scope.js +32 -0
  112. package/dist/esm/scope.js.map +1 -0
  113. package/dist/esm/web-acl-builder.d.ts +152 -0
  114. package/dist/esm/web-acl-builder.d.ts.map +1 -0
  115. package/dist/esm/web-acl-builder.js +202 -0
  116. package/dist/esm/web-acl-builder.js.map +1 -0
  117. package/package.json +78 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"names.d.ts","sourceRoot":"","sources":["../../src/names.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AA6CvD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEpD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAElD;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAOpD;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAIxD;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEtD;AAuBD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAgBnD"}
@@ -0,0 +1,132 @@
1
+ import { charSets, sanitizeString, stringConstraint, validateString, } from "@composurecdk/cloudformation";
2
+ import { Names, Token } from "aws-cdk-lib";
3
+ /**
4
+ * The name rule WAF applies to web ACLs and to the rules in them: 1–128
5
+ * characters from `[\w-]`.
6
+ */
7
+ const WAF_NAME_RULE = {
8
+ charClass: `${charSets.ALNUM}_\\-`,
9
+ minLength: 1,
10
+ maxLength: 128,
11
+ allowed: "ASCII letters, digits, _ and -",
12
+ };
13
+ /**
14
+ * AWS-property constraint for a web ACL's name. Module-private; the package
15
+ * exposes {@link validateWebAclName} through the `constraints` namespace. See
16
+ * ADR-0010.
17
+ */
18
+ const WEB_ACL_NAME = stringConstraint({
19
+ name: "WAFv2 WebACL Name",
20
+ ...WAF_NAME_RULE,
21
+ source: "https://docs.aws.amazon.com/waf/latest/APIReference/API_CreateWebACL.html",
22
+ });
23
+ /** AWS-property constraint for a rule's name within a web ACL. */
24
+ const RULE_NAME = stringConstraint({
25
+ name: "WAFv2 WebACL Rule Name",
26
+ ...WAF_NAME_RULE,
27
+ source: "https://docs.aws.amazon.com/waf/latest/APIReference/API_Rule.html",
28
+ });
29
+ /**
30
+ * AWS-property constraint for a `VisibilityConfig` metric name. The API
31
+ * accepts a looser set, but the Developer Guide restricts metric names to
32
+ * these characters, and CloudWatch is where they end up.
33
+ */
34
+ const METRIC_NAME = stringConstraint({
35
+ name: "WAFv2 VisibilityConfig MetricName",
36
+ ...WAF_NAME_RULE,
37
+ source: "https://docs.aws.amazon.com/waf/latest/APIReference/API_VisibilityConfig.html",
38
+ });
39
+ /** Metric names WAF reserves for its own aggregate metrics. */
40
+ const RESERVED_METRIC_NAMES = ["All", "Default_Action"];
41
+ /**
42
+ * Validates a web ACL name.
43
+ *
44
+ * @throws on invalid input.
45
+ */
46
+ export function validateWebAclName(raw) {
47
+ validateString(raw, WEB_ACL_NAME);
48
+ }
49
+ /**
50
+ * Validates the name of a rule in a web ACL.
51
+ *
52
+ * @throws on invalid input.
53
+ */
54
+ export function validateRuleName(raw) {
55
+ validateString(raw, RULE_NAME);
56
+ }
57
+ /**
58
+ * Validates a CloudWatch metric name for a web ACL or rule. `All` and
59
+ * `Default_Action` are rejected: WAF uses them for its own metrics.
60
+ *
61
+ * @throws on invalid input.
62
+ */
63
+ export function validateMetricName(raw) {
64
+ validateString(raw, METRIC_NAME);
65
+ if (!Token.isUnresolved(raw) && RESERVED_METRIC_NAMES.includes(raw)) {
66
+ throw new Error(`${METRIC_NAME.name} "${raw}" is reserved by AWS WAF. Reserved: ${RESERVED_METRIC_NAMES.join(", ")}. See ${METRIC_NAME.source}.`);
67
+ }
68
+ }
69
+ /**
70
+ * The web ACL's name when it is a literal, and otherwise a name derived from
71
+ * its construct path, unique in the app. The base of its default metric and
72
+ * log group names.
73
+ *
74
+ * @internal
75
+ */
76
+ export function webAclBaseName(webAcl) {
77
+ const { name } = webAcl;
78
+ if (name !== undefined && !Token.isUnresolved(name))
79
+ return name;
80
+ return Names.uniqueResourceName(webAcl, { maxLength: 128 });
81
+ }
82
+ /**
83
+ * Turns a construct path or other derived string into a legal metric name, for
84
+ * when the user has not chosen one.
85
+ *
86
+ * @internal
87
+ */
88
+ export function sanitizeMetricName(raw) {
89
+ return sanitizeString(raw, METRIC_NAME, "");
90
+ }
91
+ const LABEL_SOURCE = "https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-label-requirements.html";
92
+ /** Each colon-separated part of a rule label: a namespace or the name. */
93
+ const LABEL_PART = stringConstraint({
94
+ name: "WAFv2 rule label part",
95
+ ...WAF_NAME_RULE,
96
+ source: LABEL_SOURCE,
97
+ });
98
+ const RESERVED_LABEL_WORDS = [
99
+ "awswaf",
100
+ "aws",
101
+ "waf",
102
+ "rulegroup",
103
+ "webacl",
104
+ "regexpatternset",
105
+ "ipset",
106
+ "managed",
107
+ ];
108
+ const MAX_LABEL_NAMESPACES = 5;
109
+ /**
110
+ * Validates a label a rule adds to matching requests: up to five
111
+ * colon-separated namespaces and a name, each 1–128 characters of
112
+ * `[A-Za-z0-9_-]`, none of them one of WAF's reserved words. WAF adds the
113
+ * `awswaf:<account>:webacl:<name>:` prefix itself, so it is not part of the
114
+ * label.
115
+ *
116
+ * @throws on invalid input.
117
+ */
118
+ export function validateRuleLabel(raw) {
119
+ if (Token.isUnresolved(raw))
120
+ return;
121
+ const components = raw.split(":");
122
+ if (components.length > MAX_LABEL_NAMESPACES + 1) {
123
+ throw new Error(`WAFv2 rule label "${raw}" has ${String(components.length - 1)} namespaces; at most ${String(MAX_LABEL_NAMESPACES)} are allowed. See ${LABEL_SOURCE}.`);
124
+ }
125
+ for (const component of components) {
126
+ validateString(component, LABEL_PART);
127
+ if (RESERVED_LABEL_WORDS.includes(component)) {
128
+ throw new Error(`WAFv2 rule label "${raw}" uses "${component}", which AWS WAF reserves. Reserved: ${RESERVED_LABEL_WORDS.join(", ")}. See ${LABEL_SOURCE}.`);
129
+ }
130
+ }
131
+ }
132
+ //# sourceMappingURL=names.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"names.js","sourceRoot":"","sources":["../../src/names.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
@@ -0,0 +1,123 @@
1
+ import type { CfnWebACL } from "aws-cdk-lib/aws-wafv2";
2
+ import { COPY_STATE, type IBuilder } from "@composurecdk/core";
3
+ import type { CustomResponseBody, CustomResponseBodies } from "./custom-response.js";
4
+ import type { WebAclRuleAlarmConfig } from "./alarm-config.js";
5
+ /** The action a rule takes on a request that matches its statement. */
6
+ export type WebAclRuleAction = "block" | "allow" | "count" | "captcha" | "challenge";
7
+ /**
8
+ * Configuration for one rule in a web ACL. The statement, action, custom
9
+ * response and labels are set with the builder's methods; these are the rest.
10
+ */
11
+ export interface WebAclRuleBuilderProps {
12
+ /**
13
+ * The rule's priority. WAF evaluates rules from the lowest priority up.
14
+ * Defaults to ten times the rule's position in `addRule` order, so rules
15
+ * run in the order they are declared. Must be unique within the web ACL.
16
+ */
17
+ priority?: number;
18
+ /**
19
+ * The name of the rule's CloudWatch metrics. Defaults to the rule's name.
20
+ */
21
+ metricName?: string;
22
+ /**
23
+ * Whether WAF keeps a sample of the requests this rule matches. Defaults to
24
+ * the web ACL's `sampledRequests`, which is off.
25
+ */
26
+ sampledRequests?: boolean;
27
+ /** CAPTCHA immunity for this rule, overriding the web ACL's. */
28
+ captchaConfig?: CfnWebACL.RuleProperty["captchaConfig"];
29
+ /** Challenge immunity for this rule, overriding the web ACL's. */
30
+ challengeConfig?: CfnWebACL.RuleProperty["challengeConfig"];
31
+ /**
32
+ * This rule's recommended alarms: a `BlockedRequests` alarm, on by default
33
+ * for a rule you wrote that blocks and opt-in for a rule-group rule, and a
34
+ * `DaysToExpiry` alarm for a pinned managed rule group version. `false`
35
+ * disables both.
36
+ */
37
+ alarms?: WebAclRuleAlarmConfig | false;
38
+ }
39
+ /** What the web ACL builder knows when it turns a rule into CloudFormation. */
40
+ export interface RuleBuildContext {
41
+ /** The rule's name, as given to `addRule`. */
42
+ readonly name: string;
43
+ /** Names the web ACL in error messages. */
44
+ readonly owner: string;
45
+ /** The priority to use unless the rule sets its own. */
46
+ readonly defaultPriority: number;
47
+ /** The web ACL's sampling setting, which the rule inherits. */
48
+ readonly sampledRequests: boolean;
49
+ /** Where custom response bodies are collected. */
50
+ readonly bodies: CustomResponseBodies;
51
+ }
52
+ /** A rule turned into CloudFormation, with what the web ACL needs to know about it. */
53
+ export interface BuiltRule {
54
+ readonly property: CfnWebACL.RuleProperty & {
55
+ readonly priority: number;
56
+ };
57
+ /** The rule's CloudWatch metric name: the `Rule` dimension of its metrics. */
58
+ readonly metricName: string;
59
+ readonly sampled: boolean;
60
+ readonly rateBased: boolean;
61
+ /** Whether the rule evaluates a rule group, so takes `overrideAction`. */
62
+ readonly ruleGroup: boolean;
63
+ /** Whether matching requests are blocked: by the rule, or by a rule group's own rules. */
64
+ readonly blocks: boolean;
65
+ /** The rule's recommended alarm configuration. */
66
+ readonly alarms: WebAclRuleAlarmConfig | false | undefined;
67
+ }
68
+ declare class WebAclRuleBuilder {
69
+ #private;
70
+ props: Partial<WebAclRuleBuilderProps>;
71
+ /**
72
+ * Sets the rule's statement as raw CloudFormation, for a statement this
73
+ * builder has no helper for. Fields CDK types as `any` — `singleHeader`,
74
+ * `queryString`, `uriPath`, `method` and the like — pass through verbatim,
75
+ * so they must use CloudFormation's PascalCase (`{ Name: "x-api-key" }`).
76
+ *
77
+ * A rule has exactly one statement; setting a second throws.
78
+ */
79
+ statement(statement: CfnWebACL.StatementProperty): this;
80
+ /** Blocks matching requests. This is the default action. */
81
+ block(): this;
82
+ /** Allows matching requests, ending evaluation of the web ACL. */
83
+ allow(): this;
84
+ /**
85
+ * Counts matching requests and carries on evaluating. On a rule-group rule
86
+ * this counts every rule in the group instead of applying its actions.
87
+ */
88
+ count(): this;
89
+ /** Answers matching requests with a CAPTCHA puzzle. */
90
+ captcha(): this;
91
+ /** Answers matching requests with a silent browser challenge. */
92
+ challenge(): this;
93
+ /**
94
+ * Blocks matching requests with a custom response instead of WAF's `403`.
95
+ * The body is collected into the web ACL's `customResponseBodies`, so
96
+ * nothing has to be declared there by hand.
97
+ *
98
+ * @param status - One of the status codes WAF supports; `410` is not.
99
+ * @param body - `{ json }`, `{ text }` or `{ html }`; at most 4 KB.
100
+ * @param headers - At most 10, and not `content-type`.
101
+ */
102
+ respond(status: number, body?: CustomResponseBody, headers?: Record<string, string>): this;
103
+ /**
104
+ * Adds labels to matching requests, for later rules to match on with a
105
+ * label match statement. Give the namespaces and name only, such as
106
+ * `"auth:missing-token"`; WAF adds the web ACL prefix.
107
+ */
108
+ labels(...labels: string[]): this;
109
+ /** @internal — see ADR-0005. */
110
+ [COPY_STATE](target: WebAclRuleBuilder): void;
111
+ /** @internal — the web ACL builder turns each rule into CloudFormation. */
112
+ toRule(ctx: RuleBuildContext): BuiltRule;
113
+ }
114
+ type FullWebAclRuleBuilder = IBuilder<WebAclRuleBuilderProps, WebAclRuleBuilder>;
115
+ /**
116
+ * Fluent configuration for one rule in a web ACL, passed to the callback of
117
+ * {@link IWebAclBuilder.addRule}.
118
+ */
119
+ export type IWebAclRuleBuilder = Omit<FullWebAclRuleBuilder, "toRule">;
120
+ /** @internal — the web ACL builder makes one of these per rule. */
121
+ export declare function createWebAclRuleBuilder(): FullWebAclRuleBuilder;
122
+ export {};
123
+ //# sourceMappingURL=rule-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-builder.d.ts","sourceRoot":"","sources":["../../src/rule-builder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,160 @@
1
+ import { Builder, COPY_STATE } from "@composurecdk/core";
2
+ import { WEB_ACL_VISIBILITY_DEFAULTS } from "./defaults.js";
3
+ import { validateMetricName, validateRuleLabel } from "./names.js";
4
+ const RULE_GROUP_STATEMENTS = ["managedRuleGroupStatement", "ruleGroupReferenceStatement"];
5
+ /** Whether `statement` evaluates a rule group, so its rule takes `overrideAction`. */
6
+ function isRuleGroupStatement(statement) {
7
+ return RULE_GROUP_STATEMENTS.some((key) => statement[key] !== undefined);
8
+ }
9
+ class WebAclRuleBuilder {
10
+ props = {};
11
+ #statement;
12
+ #action;
13
+ #response;
14
+ #labels = [];
15
+ /**
16
+ * Sets the rule's statement as raw CloudFormation, for a statement this
17
+ * builder has no helper for. Fields CDK types as `any` — `singleHeader`,
18
+ * `queryString`, `uriPath`, `method` and the like — pass through verbatim,
19
+ * so they must use CloudFormation's PascalCase (`{ Name: "x-api-key" }`).
20
+ *
21
+ * A rule has exactly one statement; setting a second throws.
22
+ */
23
+ statement(statement) {
24
+ this.#setStatement("statement", statement);
25
+ return this;
26
+ }
27
+ /** Blocks matching requests. This is the default action. */
28
+ block() {
29
+ return this.#setAction("block");
30
+ }
31
+ /** Allows matching requests, ending evaluation of the web ACL. */
32
+ allow() {
33
+ return this.#setAction("allow");
34
+ }
35
+ /**
36
+ * Counts matching requests and carries on evaluating. On a rule-group rule
37
+ * this counts every rule in the group instead of applying its actions.
38
+ */
39
+ count() {
40
+ return this.#setAction("count");
41
+ }
42
+ /** Answers matching requests with a CAPTCHA puzzle. */
43
+ captcha() {
44
+ return this.#setAction("captcha");
45
+ }
46
+ /** Answers matching requests with a silent browser challenge. */
47
+ challenge() {
48
+ return this.#setAction("challenge");
49
+ }
50
+ /**
51
+ * Blocks matching requests with a custom response instead of WAF's `403`.
52
+ * The body is collected into the web ACL's `customResponseBodies`, so
53
+ * nothing has to be declared there by hand.
54
+ *
55
+ * @param status - One of the status codes WAF supports; `410` is not.
56
+ * @param body - `{ json }`, `{ text }` or `{ html }`; at most 4 KB.
57
+ * @param headers - At most 10, and not `content-type`.
58
+ */
59
+ respond(status, body, headers) {
60
+ if (this.#action !== undefined && this.#action !== "block") {
61
+ throw new Error(`respond() blocks the request, but the rule's action is already "${this.#action}". A custom response is only available on Block.`);
62
+ }
63
+ this.#action = "block";
64
+ this.#response = { status, body, headers };
65
+ return this;
66
+ }
67
+ /**
68
+ * Adds labels to matching requests, for later rules to match on with a
69
+ * label match statement. Give the namespaces and name only, such as
70
+ * `"auth:missing-token"`; WAF adds the web ACL prefix.
71
+ */
72
+ labels(...labels) {
73
+ for (const label of labels)
74
+ validateRuleLabel(label);
75
+ this.#labels.push(...labels);
76
+ return this;
77
+ }
78
+ /** @internal — see ADR-0005. */
79
+ [COPY_STATE](target) {
80
+ target.#statement = this.#statement;
81
+ target.#action = this.#action;
82
+ target.#response = this.#response;
83
+ target.#labels.push(...this.#labels);
84
+ }
85
+ #setStatement(method, statement) {
86
+ if (this.#statement !== undefined) {
87
+ throw new Error(`${method}(): the rule already has a statement. A rule has exactly one; combine conditions with an AndStatement or OrStatement.`);
88
+ }
89
+ this.#statement = statement;
90
+ }
91
+ #setAction(action) {
92
+ if (this.#response !== undefined && action !== "block") {
93
+ throw new Error(`${action}(): the rule already has a custom response from respond(), which only applies to Block.`);
94
+ }
95
+ this.#action = action;
96
+ return this;
97
+ }
98
+ /** @internal — the web ACL builder turns each rule into CloudFormation. */
99
+ toRule(ctx) {
100
+ const who = `rule "${ctx.name}"`;
101
+ const statement = this.#statement;
102
+ if (statement === undefined) {
103
+ throw new Error(`${ctx.owner} ${who} has no statement. Give it one, such as .statement({ ... }).`);
104
+ }
105
+ const metricName = this.props.metricName ?? ctx.name;
106
+ validateMetricName(metricName);
107
+ const sampled = this.props.sampledRequests ?? ctx.sampledRequests;
108
+ const rateBased = statement.rateBasedStatement !== undefined;
109
+ const ruleGroup = isRuleGroupStatement(statement);
110
+ const { blocks, ...action } = this.#actionProperty(ctx, who, ruleGroup, rateBased);
111
+ const property = {
112
+ name: ctx.name,
113
+ priority: this.props.priority ?? ctx.defaultPriority,
114
+ statement,
115
+ ...action,
116
+ visibilityConfig: {
117
+ ...WEB_ACL_VISIBILITY_DEFAULTS,
118
+ metricName,
119
+ sampledRequestsEnabled: sampled,
120
+ },
121
+ ...(this.#labels.length > 0 && { ruleLabels: this.#labels.map((name) => ({ name })) }),
122
+ ...(this.props.captchaConfig && { captchaConfig: this.props.captchaConfig }),
123
+ ...(this.props.challengeConfig && { challengeConfig: this.props.challengeConfig }),
124
+ };
125
+ return {
126
+ property,
127
+ metricName,
128
+ sampled,
129
+ rateBased,
130
+ ruleGroup,
131
+ blocks,
132
+ alarms: this.props.alarms,
133
+ };
134
+ }
135
+ #actionProperty(ctx, who, ruleGroup, rateBased) {
136
+ if (ruleGroup) {
137
+ if (this.#action !== undefined && this.#action !== "count") {
138
+ throw new Error(`${ctx.owner} ${who} evaluates a rule group, whose rules keep their own actions. ` +
139
+ `Use .count() to count them all instead, or leave the action unset.`);
140
+ }
141
+ // The group's own rules block unless the whole group is counted.
142
+ return this.#action === "count"
143
+ ? { overrideAction: { count: {} }, blocks: false }
144
+ : { overrideAction: { none: {} }, blocks: true };
145
+ }
146
+ const action = this.#action ?? "block";
147
+ if (action === "allow" && rateBased) {
148
+ throw new Error(`${ctx.owner} ${who} is rate-based, and a rate-based rule cannot Allow. Use block, count, captcha or challenge.`);
149
+ }
150
+ if (action !== "block")
151
+ return { action: { [action]: {} }, blocks: false };
152
+ return { action: { block: ctx.bodies.block(who, ctx.name, this.#response) }, blocks: true };
153
+ }
154
+ }
155
+ /** @internal — the web ACL builder makes one of these per rule. */
156
+ export function createWebAclRuleBuilder() {
157
+ // eslint-disable-next-line composurecdk/builder-must-be-tagged -- a rule is a property of AWS::WAFv2::WebACL, not a taggable resource
158
+ return Builder(WebAclRuleBuilder);
159
+ }
160
+ //# sourceMappingURL=rule-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-builder.js","sourceRoot":"","sources":["../../src/rule-builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,UAAU,EAAiB,MAAM,oBAAoB,CAAC;AAOxE,OAAO,EAAE,2BAA2B,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAwEnE,MAAM,qBAAqB,GAAG,CAAC,2BAA2B,EAAE,6BAA6B,CAAU,CAAC;AAEpG,sFAAsF;AACtF,SAAS,oBAAoB,CAAC,SAAsC;IAClE,OAAO,qBAAqB,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,SAAS,CAAC,CAAC;AAC3E,CAAC;AAED,MAAM,iBAAiB;IACrB,KAAK,GAAoC,EAAE,CAAC;IAC5C,UAAU,CAA+B;IACzC,OAAO,CAAoB;IAC3B,SAAS,CAAkB;IAClB,OAAO,GAAa,EAAE,CAAC;IAEhC;;;;;;;OAOG;IACH,SAAS,CAAC,SAAsC;QAC9C,IAAI,CAAC,aAAa,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;QAC3C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,4DAA4D;IAC5D,KAAK;QACH,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC;IAED,kEAAkE;IAClE,KAAK;QACH,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC;IAED;;;OAGG;IACH,KAAK;QACH,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC;IAED,uDAAuD;IACvD,OAAO;QACL,OAAO,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IACpC,CAAC;IAED,iEAAiE;IACjE,SAAS;QACP,OAAO,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;IACtC,CAAC;IAED;;;;;;;;OAQG;IACH,OAAO,CAAC,MAAc,EAAE,IAAyB,EAAE,OAAgC;QACjF,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,IAAI,IAAI,CAAC,OAAO,KAAK,OAAO,EAAE,CAAC;YAC3D,MAAM,IAAI,KAAK,CACb,mEAAmE,IAAI,CAAC,OAAO,kDAAkD,CAClI,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,SAAS,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QAC3C,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,GAAG,MAAgB;QACxB,KAAK,MAAM,KAAK,IAAI,MAAM;YAAE,iBAAiB,CAAC,KAAK,CAAC,CAAC;QACrD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,gCAAgC;IAChC,CAAC,UAAU,CAAC,CAAC,MAAyB;QACpC,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;QACpC,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAC9B,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;QAClC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC;IAED,aAAa,CAAC,MAAc,EAAE,SAAsC;QAClE,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CACb,GAAG,MAAM,uHAAuH,CACjI,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;IAC9B,CAAC;IAED,UAAU,CAAC,MAAwB;QACjC,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;YACvD,MAAM,IAAI,KAAK,CACb,GAAG,MAAM,yFAAyF,CACnG,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,2EAA2E;IAC3E,MAAM,CAAC,GAAqB;QAC1B,MAAM,GAAG,GAAG,SAAS,GAAG,CAAC,IAAI,GAAG,CAAC;QACjC,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC;QAClC,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CACb,GAAG,GAAG,CAAC,KAAK,IAAI,GAAG,8DAA8D,CAClF,CAAC;QACJ,CAAC;QAED,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,IAAI,GAAG,CAAC,IAAI,CAAC;QACrD,kBAAkB,CAAC,UAAU,CAAC,CAAC;QAC/B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,IAAI,GAAG,CAAC,eAAe,CAAC;QAClE,MAAM,SAAS,GAAG,SAAS,CAAC,kBAAkB,KAAK,SAAS,CAAC;QAC7D,MAAM,SAAS,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;QAElD,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;QACnF,MAAM,QAAQ,GAAG;YACf,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,GAAG,CAAC,eAAe;YACpD,SAAS;YACT,GAAG,MAAM;YACT,gBAAgB,EAAE;gBAChB,GAAG,2BAA2B;gBAC9B,UAAU;gBACV,sBAAsB,EAAE,OAAO;aAChC;YACD,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;YACtF,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,IAAI,EAAE,aAAa,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC;YAC5E,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,eAAe,IAAI,EAAE,eAAe,EAAE,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC;SACnF,CAAC;QACF,OAAO;YACL,QAAQ;YACR,UAAU;YACV,OAAO;YACP,SAAS;YACT,SAAS;YACT,MAAM;YACN,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM;SAC1B,CAAC;IACJ,CAAC;IAED,eAAe,CACb,GAAqB,EACrB,GAAW,EACX,SAAkB,EAClB,SAAkB;QAElB,IAAI,SAAS,EAAE,CAAC;YACd,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,IAAI,IAAI,CAAC,OAAO,KAAK,OAAO,EAAE,CAAC;gBAC3D,MAAM,IAAI,KAAK,CACb,GAAG,GAAG,CAAC,KAAK,IAAI,GAAG,+DAA+D;oBAChF,oEAAoE,CACvE,CAAC;YACJ,CAAC;YACD,iEAAiE;YACjE,OAAO,IAAI,CAAC,OAAO,KAAK,OAAO;gBAC7B,CAAC,CAAC,EAAE,cAAc,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;gBAClD,CAAC,CAAC,EAAE,cAAc,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;QACrD,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC;QACvC,IAAI,MAAM,KAAK,OAAO,IAAI,SAAS,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CACb,GAAG,GAAG,CAAC,KAAK,IAAI,GAAG,6FAA6F,CACjH,CAAC;QACJ,CAAC;QACD,IAAI,MAAM,KAAK,OAAO;YAAE,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QAC3E,OAAO,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC9F,CAAC;CACF;AAWD,mEAAmE;AACnE,MAAM,UAAU,uBAAuB;IACrC,sIAAsI;IACtI,OAAO,OAAO,CAA4C,iBAAiB,CAAC,CAAC;AAC/E,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { IConstruct } from "constructs";
2
+ /** Where a web ACL can be used: CloudFront distributions, or regional resources. */
3
+ export type WebAclScope = "REGIONAL" | "CLOUDFRONT";
4
+ /**
5
+ * Throws when a CLOUDFRONT web ACL's stack is in a known region other than
6
+ * `us-east-1`, and warns when the region cannot be known at synth.
7
+ *
8
+ * Env-agnostic stacks fall back to `CDK_DEFAULT_REGION`, which the CDK toolkit
9
+ * uses to fill the deploy region, as `@composurecdk/route53`'s query logging
10
+ * does.
11
+ *
12
+ * @internal
13
+ */
14
+ export declare function checkCloudFrontRegion(scope: IConstruct, id: string): void;
15
+ //# sourceMappingURL=scope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../../src/scope.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,oFAAoF;AACpF,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,YAAY,CAAC;AAOpD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAqBzE"}
@@ -0,0 +1,32 @@
1
+ import { Annotations, Stack, Token } from "aws-cdk-lib";
2
+ const CLOUDFRONT_REGION = "us-east-1";
3
+ const REGION_ANNOTATION = "@composurecdk/wafv2:cloudfront-region";
4
+ const SOURCE = "https://docs.aws.amazon.com/waf/latest/developerguide/how-aws-waf-works-resources.html";
5
+ /**
6
+ * Throws when a CLOUDFRONT web ACL's stack is in a known region other than
7
+ * `us-east-1`, and warns when the region cannot be known at synth.
8
+ *
9
+ * Env-agnostic stacks fall back to `CDK_DEFAULT_REGION`, which the CDK toolkit
10
+ * uses to fill the deploy region, as `@composurecdk/route53`'s query logging
11
+ * does.
12
+ *
13
+ * @internal
14
+ */
15
+ export function checkCloudFrontRegion(scope, id) {
16
+ const stackRegion = Stack.of(scope).region;
17
+ const region = Token.isUnresolved(stackRegion) ? process.env.CDK_DEFAULT_REGION : stackRegion;
18
+ if (region === undefined || region === "") {
19
+ Annotations.of(scope).addWarningV2(REGION_ANNOTATION, `WebAclBuilder "${id}" has scope CLOUDFRONT, which AWS WAF only accepts in ` +
20
+ `${CLOUDFRONT_REGION}. This stack is env-agnostic and CDK_DEFAULT_REGION is not ` +
21
+ `set, so the region cannot be checked at synth. Pin env: { region: ` +
22
+ `"${CLOUDFRONT_REGION}" } on the stack to silence this warning.`);
23
+ return;
24
+ }
25
+ if (region !== CLOUDFRONT_REGION) {
26
+ throw new Error(`WebAclBuilder "${id}" has scope CLOUDFRONT, but its stack is in "${region}". ` +
27
+ `AWS WAF only creates CloudFront web ACLs in ${CLOUDFRONT_REGION}. Deploy this ` +
28
+ `web ACL from a ${CLOUDFRONT_REGION} stack and pass its ARN to the distribution's ` +
29
+ `webAclId. See ${SOURCE}.`);
30
+ }
31
+ }
32
+ //# sourceMappingURL=scope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope.js","sourceRoot":"","sources":["../../src/scope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAMxD,MAAM,iBAAiB,GAAG,WAAW,CAAC;AACtC,MAAM,iBAAiB,GAAG,uCAAuC,CAAC;AAClE,MAAM,MAAM,GACV,wFAAwF,CAAC;AAE3F;;;;;;;;;GASG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAiB,EAAE,EAAU;IACjE,MAAM,WAAW,GAAG,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC;IAC3C,MAAM,MAAM,GAAG,KAAK,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAC,WAAW,CAAC;IAC9F,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,EAAE,EAAE,CAAC;QAC1C,WAAW,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,YAAY,CAChC,iBAAiB,EACjB,kBAAkB,EAAE,wDAAwD;YAC1E,GAAG,iBAAiB,6DAA6D;YACjF,oEAAoE;YACpE,IAAI,iBAAiB,2CAA2C,CACnE,CAAC;QACF,OAAO;IACT,CAAC;IACD,IAAI,MAAM,KAAK,iBAAiB,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CACb,kBAAkB,EAAE,gDAAgD,MAAM,KAAK;YAC7E,+CAA+C,iBAAiB,gBAAgB;YAChF,kBAAkB,iBAAiB,gDAAgD;YACnF,iBAAiB,MAAM,GAAG,CAC7B,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,152 @@
1
+ import type { Alarm } from "aws-cdk-lib/aws-cloudwatch";
2
+ import type { LogGroup } from "aws-cdk-lib/aws-logs";
3
+ import { type CfnLoggingConfiguration, CfnWebACL, type CfnWebACLAssociation, type CfnWebACLProps } from "aws-cdk-lib/aws-wafv2";
4
+ import type { IConstruct } from "constructs";
5
+ import { COPY_STATE, type Lifecycle, type Resolvable } from "@composurecdk/core";
6
+ import { type ITaggedBuilder } from "@composurecdk/cloudformation";
7
+ import { type AlarmConfigurer } from "@composurecdk/cloudwatch";
8
+ import type { WebAclAlarmConfig } from "./alarm-config.js";
9
+ import { type WebAclAssociationTarget } from "./association.js";
10
+ import { type CustomResponse } from "./custom-response.js";
11
+ import { type IWebAclRuleBuilder } from "./rule-builder.js";
12
+ import { type WebAclLoggingConfig } from "./logging.js";
13
+ import { type WebAclMetrics } from "./metrics.js";
14
+ import { type WebAclScope } from "./scope.js";
15
+ /**
16
+ * Configuration for {@link createWebAclBuilder}: CDK's `CfnWebACLProps`, less
17
+ * what the builder owns. Rules, the default action and associations have
18
+ * their own methods; custom response bodies and the visibility config are
19
+ * generated.
20
+ */
21
+ export interface WebAclBuilderProps extends Omit<CfnWebACLProps, "scope" | "defaultAction" | "rules" | "customResponseBodies" | "visibilityConfig" | "tags"> {
22
+ /**
23
+ * `REGIONAL` (the default) for API Gateway, Application Load Balancers,
24
+ * AppSync, Cognito, App Runner and Verified Access; `CLOUDFRONT` for
25
+ * CloudFront distributions, which must be deployed in `us-east-1`.
26
+ */
27
+ scope?: WebAclScope;
28
+ /**
29
+ * The name of the web ACL's CloudWatch metrics. Defaults to its `name` when
30
+ * that is set, and otherwise to a name derived from its construct path, so
31
+ * two web ACLs never share metrics.
32
+ */
33
+ metricName?: string;
34
+ /**
35
+ * Whether WAF keeps samples of the requests the web ACL and its rules
36
+ * inspect. Off by default: samples keep every header, and logging's
37
+ * redaction does not apply to them. Turning it on without a
38
+ * `dataProtectionConfig` warns.
39
+ */
40
+ sampledRequests?: boolean;
41
+ /**
42
+ * The web ACL's logging. On by default, to a created `aws-waf-logs-`
43
+ * CloudWatch log group, keeping only the requests WAF does not allow and
44
+ * redacting the `authorization` and `cookie` headers. `false` turns it off.
45
+ *
46
+ * @see https://docs.aws.amazon.com/wellarchitected/latest/security-pillar/sec_detect_investigate_events_app_service_logging.html
47
+ */
48
+ logging?: WebAclLoggingConfig | false;
49
+ /**
50
+ * The master switch for the recommended alarms, which are configured per
51
+ * rule with the rule builder's `.alarms()`. `false` creates none; alarms
52
+ * added with `addAlarm()` are unaffected.
53
+ *
54
+ * No alarm actions are configured; apply them from the result or with
55
+ * `alarmActionsPolicy`.
56
+ */
57
+ recommendedAlarms?: WebAclAlarmConfig | false;
58
+ }
59
+ /** The build output of an {@link IWebAclBuilder}. */
60
+ export interface WebAclBuilderResult {
61
+ /** The web ACL. */
62
+ webAcl: CfnWebACL;
63
+ /**
64
+ * One association per `associateWith` call, in that order. Empty for a
65
+ * CLOUDFRONT web ACL, which a distribution references by `webAclId`.
66
+ */
67
+ associations: CfnWebACLAssociation[];
68
+ /** The log group WAF writes to. Absent when logging is off or goes to your own destination. */
69
+ logGroup?: LogGroup;
70
+ /** The logging configuration. Absent when logging is off. */
71
+ logging?: CfnLoggingConfiguration;
72
+ /**
73
+ * The web ACL's metrics, for the ACL, the default action and each rule,
74
+ * with dimensions that match what WAF publishes.
75
+ */
76
+ metrics: WebAclMetrics;
77
+ /** The CloudWatch alarms created, recommended and custom, keyed by alarm key. */
78
+ alarms: Record<string, Alarm>;
79
+ }
80
+ /**
81
+ * A fluent builder for an AWS WAF web ACL.
82
+ *
83
+ * @see {@link createWebAclBuilder}
84
+ */
85
+ export type IWebAclBuilder = ITaggedBuilder<WebAclBuilderProps, WebAclBuilder>;
86
+ declare class WebAclBuilder implements Lifecycle<WebAclBuilderResult> {
87
+ #private;
88
+ props: Partial<WebAclBuilderProps>;
89
+ /**
90
+ * What happens to a request no rule decides on. `allow` is the default;
91
+ * `block` can answer with a custom response.
92
+ */
93
+ defaultAction(action: "allow" | "block", response?: CustomResponse): this;
94
+ /**
95
+ * Adds a rule. Rules are evaluated in the order they are added, unless a
96
+ * rule sets its own `.priority(n)`.
97
+ *
98
+ * @param name - Unique within the web ACL; also the rule's default metric name.
99
+ * @param configure - Sets the rule's statement, action and the rest.
100
+ */
101
+ addRule(name: string, configure: (rule: IWebAclRuleBuilder) => IWebAclRuleBuilder): this;
102
+ /**
103
+ * Protects a regional resource with this web ACL. Call it once per
104
+ * resource. Accepts an ARN, a CDK reference CloudFormation's
105
+ * `AWS::WAFv2::WebACLAssociation` takes (an Application Load Balancer, an
106
+ * AppSync API), or an API Gateway stage, as a concrete value or a `ref`.
107
+ *
108
+ * A CLOUDFRONT web ACL cannot be associated: pass its ARN to the
109
+ * distribution's `webAclId` instead.
110
+ */
111
+ associateWith(target: Resolvable<WebAclAssociationTarget>): this;
112
+ /**
113
+ * Adds a custom alarm. The metric factory receives the web ACL's
114
+ * {@link WebAclMetrics}, so a rule's metrics come by its name:
115
+ * `(m) => m.rule("PerIpRate").countedRequests()`.
116
+ */
117
+ addAlarm(key: string, configure: AlarmConfigurer<WebAclMetrics>): this;
118
+ /**
119
+ * Adds a set of custom alarms in one call: each entry exactly as if by
120
+ * `addAlarm(key, configure)`, in the record's iteration order.
121
+ */
122
+ addAlarms(alarms: Record<string, AlarmConfigurer<WebAclMetrics>>): this;
123
+ /** @internal — see ADR-0005. */
124
+ [COPY_STATE](target: WebAclBuilder): void;
125
+ build(scope: IConstruct, id: string, context?: Record<string, object>): WebAclBuilderResult;
126
+ }
127
+ /**
128
+ * Creates a builder for an AWS WAF web ACL, with its rules, its associations
129
+ * and the visibility settings the rest of the package relies on.
130
+ *
131
+ * By default the web ACL is regional, allows what no rule blocks, publishes
132
+ * CloudWatch metrics for itself and every rule ({@link WEB_ACL_VISIBILITY_DEFAULTS}),
133
+ * and keeps no sampled requests ({@link WEB_ACL_DEFAULTS}).
134
+ *
135
+ * @example
136
+ * ```ts
137
+ * createWebAclBuilder()
138
+ * .associateWith(ref("api", (r: RestApiBuilderResult) => r.api.deploymentStage))
139
+ * .addRule("BlockAdmin", (rule) =>
140
+ * rule.statement({
141
+ * byteMatchStatement: {
142
+ * fieldToMatch: { uriPath: {} },
143
+ * positionalConstraint: "STARTS_WITH",
144
+ * searchString: "/admin",
145
+ * textTransformations: [{ priority: 0, type: "NONE" }],
146
+ * },
147
+ * }).respond(404));
148
+ * ```
149
+ */
150
+ export declare function createWebAclBuilder(): IWebAclBuilder;
151
+ export {};
152
+ //# sourceMappingURL=web-acl-builder.d.ts.map