1 | |
2 | type Vercel = { |
3 | token: string; |
4 | }; |
5 | |
6 | * Update Firewall Configuration |
7 | * Process updates to modify the existing firewall config for a project |
8 | */ |
9 | export async function main( |
10 | auth: Vercel, |
11 | projectId: string | undefined, |
12 | teamId: string | undefined, |
13 | slug: string | undefined, |
14 | body: |
15 | | { action: "firewallEnabled"; id?: null; value: false | true } |
16 | | { |
17 | action: "rules.insert"; |
18 | id?: null; |
19 | value: { |
20 | name: string; |
21 | description?: string; |
22 | active: false | true; |
23 | conditionGroup: { |
24 | conditions: { |
25 | type: |
26 | | "host" |
27 | | "path" |
28 | | "method" |
29 | | "header" |
30 | | "query" |
31 | | "cookie" |
32 | | "target_path" |
33 | | "raw_path" |
34 | | "ip_address" |
35 | | "region" |
36 | | "protocol" |
37 | | "scheme" |
38 | | "environment" |
39 | | "user_agent" |
40 | | "geo_continent" |
41 | | "geo_country" |
42 | | "geo_country_region" |
43 | | "geo_city" |
44 | | "geo_as_number" |
45 | | "ja4_digest" |
46 | | "ja3_digest" |
47 | | "rate_limit_api_id"; |
48 | op: |
49 | | "re" |
50 | | "eq" |
51 | | "neq" |
52 | | "ex" |
53 | | "nex" |
54 | | "inc" |
55 | | "ninc" |
56 | | "pre" |
57 | | "suf" |
58 | | "sub" |
59 | | "gt" |
60 | | "gte" |
61 | | "lt" |
62 | | "lte"; |
63 | neg?: false | true; |
64 | key?: string; |
65 | value?: string | number | string[]; |
66 | }[]; |
67 | }[]; |
68 | action: { |
69 | mitigate?: { |
70 | action: |
71 | | "log" |
72 | | "challenge" |
73 | | "deny" |
74 | | "bypass" |
75 | | "rate_limit" |
76 | | "redirect"; |
77 | rateLimit?: { |
78 | algo: "fixed_window" | "token_bucket"; |
79 | window: number; |
80 | limit: number; |
81 | keys: string[]; |
82 | action?: "log" | "challenge" | "deny" | "rate_limit"; |
83 | }; |
84 | redirect?: { location: string; permanent: false | true }; |
85 | actionDuration?: string; |
86 | bypassSystem?: false | true; |
87 | }; |
88 | }; |
89 | }; |
90 | } |
91 | | { |
92 | action: "rules.update"; |
93 | id: string; |
94 | value: { |
95 | name: string; |
96 | description?: string; |
97 | active: false | true; |
98 | conditionGroup: { |
99 | conditions: { |
100 | type: |
101 | | "host" |
102 | | "path" |
103 | | "method" |
104 | | "header" |
105 | | "query" |
106 | | "cookie" |
107 | | "target_path" |
108 | | "raw_path" |
109 | | "ip_address" |
110 | | "region" |
111 | | "protocol" |
112 | | "scheme" |
113 | | "environment" |
114 | | "user_agent" |
115 | | "geo_continent" |
116 | | "geo_country" |
117 | | "geo_country_region" |
118 | | "geo_city" |
119 | | "geo_as_number" |
120 | | "ja4_digest" |
121 | | "ja3_digest" |
122 | | "rate_limit_api_id"; |
123 | op: |
124 | | "re" |
125 | | "eq" |
126 | | "neq" |
127 | | "ex" |
128 | | "nex" |
129 | | "inc" |
130 | | "ninc" |
131 | | "pre" |
132 | | "suf" |
133 | | "sub" |
134 | | "gt" |
135 | | "gte" |
136 | | "lt" |
137 | | "lte"; |
138 | neg?: false | true; |
139 | key?: string; |
140 | value?: string | number | string[]; |
141 | }[]; |
142 | }[]; |
143 | action: { |
144 | mitigate?: { |
145 | action: |
146 | | "log" |
147 | | "challenge" |
148 | | "deny" |
149 | | "bypass" |
150 | | "rate_limit" |
151 | | "redirect"; |
152 | rateLimit?: { |
153 | algo: "fixed_window" | "token_bucket"; |
154 | window: number; |
155 | limit: number; |
156 | keys: string[]; |
157 | action?: "log" | "challenge" | "deny" | "rate_limit"; |
158 | }; |
159 | redirect?: { location: string; permanent: false | true }; |
160 | actionDuration?: string; |
161 | bypassSystem?: false | true; |
162 | }; |
163 | }; |
164 | }; |
165 | } |
166 | | { action: "rules.remove"; id: string; value?: null } |
167 | | { action: "rules.priority"; id: string; value: number } |
168 | | { |
169 | action: "crs.update"; |
170 | id: |
171 | | "sd" |
172 | | "ma" |
173 | | "lfi" |
174 | | "rfi" |
175 | | "rce" |
176 | | "php" |
177 | | "gen" |
178 | | "xss" |
179 | | "sqli" |
180 | | "sf" |
181 | | "java"; |
182 | value: { active: false | true; action: "log" | "deny" }; |
183 | } |
184 | | { action: "crs.disable"; id?: null; value?: null } |
185 | | { |
186 | action: "ip.insert"; |
187 | id?: null; |
188 | value: { |
189 | hostname: string; |
190 | ip: string; |
191 | notes?: string; |
192 | action: "log" | "challenge" | "deny" | "bypass"; |
193 | }; |
194 | } |
195 | | { |
196 | action: "ip.update"; |
197 | id: string; |
198 | value: { |
199 | hostname: string; |
200 | ip: string; |
201 | notes?: string; |
202 | action: "log" | "challenge" | "deny" | "bypass"; |
203 | }; |
204 | } |
205 | | { action: "ip.remove"; id: string; value?: null } |
206 | | { |
207 | action: "managedRules.update"; |
208 | id: string; |
209 | value: { action?: "log" | "challenge" | "deny"; active: false | true }; |
210 | } |
211 | | { action: "managedRuleGroup.update"; id: string; value: {} }, |
212 | ) { |
213 | const url = new URL(`https://api.vercel.com/v1/security/firewall/config`); |
214 | for (const [k, v] of [ |
215 | ["projectId", projectId], |
216 | ["teamId", teamId], |
217 | ["slug", slug], |
218 | ]) { |
219 | if (v !== undefined && v !== "" && k !== undefined) { |
220 | url.searchParams.append(k, v); |
221 | } |
222 | } |
223 | const response = await fetch(url, { |
224 | method: "PATCH", |
225 | headers: { |
226 | "Content-Type": "application/json", |
227 | Authorization: "Bearer " + auth.token, |
228 | }, |
229 | body: JSON.stringify(body), |
230 | }); |
231 | if (!response.ok) { |
232 | const text = await response.text(); |
233 | throw new Error(`${response.status} ${text}`); |
234 | } |
235 | return await response.json(); |
236 | } |
237 |
|