Skip to content

Commit 98d49c8

Browse files
committed
fix(cloudflare): correct verified API defects and stop filters leaking into writes
Independent re-validation of all 48 tools against developers.cloudflare.com turned up defects that the shipped tools would have hit on their happy path. Delete DNS record reported every success as a failure. That endpoint is the one Cloudflare v4 response with no envelope — its documented body is `{"result":{"id":...}}` with no `success` — so `!data.success` was always true. Branch on an explicit `=== false` instead. The two replace-semantics PATCH endpoints could silently destroy live config. Update rate limit rule defaulted a missing action to `block`, converting an existing `log` or challenge rule into a hard block on real traffic; update ruleset rule left action and expression optional and had no `ratelimit` or `logging` passthrough, so updating a rate limiting rule stopped it rate limiting. Both now require the fields the replacement needs, and the ruleset rule carries the two nested objects through. Access applications were unbuildable for most types: `domain` was required, but it does not exist on the saas, app_launcher, warp, biso, dash_sso, infrastructure, mcp, mcp_portal, or proxy_endpoint request variants. The application type enum was also six values behind. Access group `is_default` is an array of rule objects, not a boolean. Purge cache merged every supplied target into one body, but the purge body is a one-of over the five target kinds; it now names the conflict instead. The remaining fixes are documentation drift: the priority field is MX and URI only (an SRV record carries priority inside its content), the certificate status filter documents only "all", the Worker tag filter takes tag:allowed pairs, and the managed-rule override list conflated the DDoS-only sensitivity_level with the WAF rule-level set. Separately, controls that share a subBlock id share one stored value, and `shouldSerializeSubBlock` short-circuits on `mode: 'advanced'` before it evaluates `condition` — so a hidden list filter was reaching a write. A `list_dns_records` content filter could overwrite a record's content, cache tags could be written onto a DNS record, and the zone status enum could reach the tunnel list, whose enum is disjoint. Filters that differ from the value they collided with now carry their own id, remapped through one table before any coercion. Sharings that mean the same thing everywhere are unchanged. Aliases are cleared by explicit assignment rather than destructuring, because the executor merges the mapper's output over the raw inputs and a merely omitted key survives as its raw subBlock string. The tests assert on that merged result, and three mechanical invariants now go red on a new collision: no id spans a read filter and a written value, no dropdown id carries two option sets, and no hidden advanced control feeds an operation that cannot render it. That last one found the name filter reaching three list operations.
1 parent e79cd28 commit 98d49c8

21 files changed

Lines changed: 430 additions & 155 deletions

apps/docs/content/docs/en/integrations/cloudflare.mdx

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ Creates a new DNS record for a zone.
282282
| `content` | string | Yes | DNS record content \(e.g., IP address for A records, target for CNAME\) |
283283
| `ttl` | number | No | Time to live in seconds \(1 = automatic, default: 1\) |
284284
| `proxied` | boolean | No | Whether to enable Cloudflare proxy \(default: false\) |
285-
| `priority` | number | No | Priority for MX and SRV records |
285+
| `priority` | number | No | Record priority. Cloudflare accepts this top-level field for MX and URI records only; an SRV record carries its priority, weight, port, and target inside the record content instead |
286286
| `comment` | string | No | Comment for the DNS record |
287287
| `tags` | string | No | Comma-separated tags for the DNS record |
288288
| `apiKey` | string | Yes | Cloudflare API Token |
@@ -301,7 +301,7 @@ Creates a new DNS record for a zone.
301301
| `proxied` | boolean | Whether Cloudflare proxy is enabled |
302302
| `ttl` | number | Time to live in seconds \(1 = automatic\) |
303303
| `locked` | boolean | Whether the record is locked |
304-
| `priority` | number | Priority for MX and SRV records |
304+
| `priority` | number | Record priority, returned for MX and URI records |
305305
| `comment` | string | Comment associated with the record |
306306
| `tags` | array | Tags associated with the record |
307307
| `comment_modified_on` | string | ISO 8601 timestamp when the comment was last modified |
@@ -382,7 +382,7 @@ Lists SSL/TLS certificate packs for a zone.
382382
| Parameter | Type | Required | Description |
383383
| --------- | ---- | -------- | ----------- |
384384
| `zoneId` | string | Yes | The zone ID to list certificates for |
385-
| `status` | string | No | Filter certificate packs by status \(e.g., "all", "active", "pending"\) |
385+
| `status` | string | No | Set to "all" to include every certificate pack regardless of status. Cloudflare documents no other value for this filter; omitting it returns only active packs |
386386
| `page` | number | No | Page number of paginated results \(default: 1\) |
387387
| `per_page` | number | No | Number of certificate packs per page \(default: 20, min: 5, max: 50\) |
388388
| `deploy` | string | No | Filter by deployment environment: "staging" or "production" |
@@ -763,12 +763,14 @@ Updates a rule in a zone ruleset. Cloudflare replaces the rule definition rather
763763
| `zoneId` | string | Yes | The zone ID that owns the ruleset |
764764
| `rulesetId` | string | Yes | The ruleset ID containing the rule |
765765
| `ruleId` | string | Yes | The rule ID to update |
766-
| `action` | string | No | The action the rule performs, e.g. block, challenge, js_challenge, managed_challenge, log, skip, or execute |
767-
| `expression` | string | No | Cloudflare filter expression selecting matching requests |
766+
| `action` | string | Yes | The action the rule performs, e.g. block, challenge, js_challenge, managed_challenge, log, skip, or execute. Required because this endpoint replaces the rule definition — omitting it resets the stored action |
767+
| `expression` | string | Yes | Cloudflare filter expression selecting matching requests. Required because this endpoint replaces the rule definition — omitting it resets the stored expression |
768768
| `description` | string | No | Human-readable description of the rule |
769769
| `enabled` | boolean | No | Whether the rule is enabled |
770770
| `ref` | string | No | Reference tag that stays stable across rule updates |
771-
| `actionParameters` | string | No | JSON object of action-specific parameters, e.g. \{"id":"<MANAGED_RULESET_ID>","overrides":\{"rules":\[\{"id":"<RULE_ID>","action":"log","enabled":true\}\]\}\} |
771+
| `actionParameters` | string | No | JSON object of action-specific parameters, e.g. \{"id":"<MANAGED_RULESET_ID>","overrides":\{"rules":\[\{"id":"<RULE_ID>","action":"log","enabled":true,"score_threshold":40\}\]\}\} |
772+
| `ratelimit` | string | No | JSON rate limiting configuration to preserve on a rule in the http_ratelimit phase, e.g. \{"characteristics":\["cf.colo.id","ip.src"\],"period":60,"requests_per_period":100\}. Because the update replaces the rule, omitting this on a rate limiting rule stops it rate limiting |
773+
| `logging` | string | No | JSON logging configuration to preserve, e.g. \{"enabled":true\}. Omitting it on a rule that had logging configured resets it to the default |
772774
| `apiKey` | string | Yes | Cloudflare API Token |
773775

774776
#### Output
@@ -856,7 +858,7 @@ Lists the WAF managed rulesets deployed on a zone together with the overrides ap
856858
|`description` | string | Description of the deployment rule |
857859
|`expression` | string | Filter expression scoping which requests the managed ruleset runs on |
858860
|`enabled` | boolean | Whether the deployment is enabled |
859-
|`overrides` | json | Overrides applied to the managed ruleset, at three levelsruleset \(top level\), categories, and rules. The Rulesets engine documents action and enabled as the properties overridable at every level; individual managed rulesets may add more, and the OWASP Core Ruleset also accepts score_threshold on a rule override |
861+
|`overrides` | json | Overrides applied to the managed ruleset, at three levels. Cloudflare documents action, enabled, and sensitivity_level at the ruleset \(top\) level; category, action, enabled, and sensitivity_level per category; and id, action, enabled, score_threshold, and sensitivity_level per rule. Rule overrides beat category overrides, which beat the ruleset-level override. sensitivity_level applies only to the DDoS phases, so for a WAF managed ruleset the rule-level properties are action, enabled, and score_threshold |
860862
| `total_count` | number | Number of managed ruleset deployments found |
861863

862864
### Cloudflare List Rate Limiting Rules
@@ -957,7 +959,7 @@ Updates a rate limiting rule in the http_ratelimit phase entry point ruleset of
957959
| `characteristics` | string | Yes | Comma-separated counting characteristics. cf.colo.id is mandatory, plus exactly one of ip.src or cf.unique_visitor_id |
958960
| `period` | number | Yes | Counting window in seconds. Cloudflare accepts only 10, 60, 120, 300, 600, or 3600 |
959961
| `requestsPerPeriod` | number | Yes | Number of requests allowed within the counting period before the action fires |
960-
| `action` | string | No | Action applied once the limit is exceeded, e.g. block, managed_challenge, js_challenge, challenge, or log. Defaults to block |
962+
| `action` | string | Yes | Action applied once the limit is exceeded: block, managed_challenge, js_challenge, challenge, or log. Required because this endpoint replaces the rule rather than merging into it — a defaulted action would silently convert an existing log or challenge rule into a hard block |
961963
| `mitigationTimeout` | number | No | Seconds the action stays applied. Cloudflare accepts only 0, 10, 60, 120, 300, 600, 3600, or 86400 |
962964
| `counting_expression` | string | No | Optional expression defining which requests are counted |
963965
| `requestsToOrigin` | boolean | No | When true, only requests that reach the origin are counted |
@@ -1073,8 +1075,8 @@ Creates a Cloudflare Access (Zero Trust) application that puts an identity check
10731075
| Parameter | Type | Required | Description |
10741076
| --------- | ---- | -------- | ----------- |
10751077
| `accountId` | string | Yes | The Cloudflare account ID. Access applications are account-scoped |
1076-
| `type` | string | Yes | Application type, e.g. self_hosted, saas, ssh, vnc, app_launcher, warp, or bookmark |
1077-
| `domain` | string | Yes | The primary hostname and path secured by Access, e.g. internal.example.com or example.com/admin |
1078+
| `type` | string | Yes | Application type: self_hosted, saas, ssh, vnc, app_launcher, warp, biso, bookmark, dash_sso, infrastructure, rdp, mcp, mcp_portal, or proxy_endpoint |
1079+
| `domain` | string | No | The primary hostname and path secured by Access, e.g. internal.example.com or example.com/admin. Required for the self_hosted, ssh, vnc, rdp, and bookmark types; the saas, app_launcher, warp, biso, dash_sso, infrastructure, mcp, mcp_portal, and proxy_endpoint types do not accept it |
10781080
| `name` | string | No | Friendly name shown in the dashboard and App Launcher |
10791081
| `sessionDuration` | string | No | How long an Access session stays valid, e.g. 24h or 30m |
10801082
| `allowedIdps` | string | No | Comma-separated identity provider IDs users may authenticate with. Leave empty to allow all configured providers |
@@ -1118,8 +1120,8 @@ Updates a Cloudflare Access (Zero Trust) application. This replaces the applicat
11181120
| --------- | ---- | -------- | ----------- |
11191121
| `accountId` | string | Yes | The Cloudflare account ID. Access applications are account-scoped |
11201122
| `appId` | string | Yes | The Access application ID to update |
1121-
| `type` | string | Yes | Application type, e.g. self_hosted, saas, ssh, vnc, app_launcher, warp, or bookmark |
1122-
| `domain` | string | Yes | The primary hostname and path secured by Access |
1123+
| `type` | string | Yes | Application type: self_hosted, saas, ssh, vnc, app_launcher, warp, biso, bookmark, dash_sso, infrastructure, rdp, mcp, mcp_portal, or proxy_endpoint |
1124+
| `domain` | string | No | The primary hostname and path secured by Access. Required for the self_hosted, ssh, vnc, rdp, and bookmark types; the saas, app_launcher, warp, biso, dash_sso, infrastructure, mcp, mcp_portal, and proxy_endpoint types do not accept it |
11231125
| `name` | string | No | Friendly name shown in the dashboard and App Launcher |
11241126
| `sessionDuration` | string | No | How long an Access session stays valid, e.g. 24h or 30m |
11251127
| `allowedIdps` | string | No | Comma-separated identity provider IDs users may authenticate with |
@@ -1332,7 +1334,7 @@ Lists the reusable Cloudflare Access (Zero Trust) groups in an account. Groups b
13321334
| `groups` | array | Access groups in the account |
13331335
|`id` | string | Access group identifier |
13341336
|`name` | string | Group name |
1335-
|`is_default` | boolean | Whether the group is automatically applied to every new application |
1337+
|`is_default` | json | Rules that place this group in every Access application by default. Cloudflare returns an array of rule objects here, not a boolean |
13361338
|`include` | json | Rules evaluated with OR logic |
13371339
|`exclude` | json | Rules evaluated with NOT logic |
13381340
|`require` | json | Rules evaluated with AND logic |
@@ -1554,7 +1556,7 @@ Lists the Workers scripts deployed in an account. Requires an API token with Acc
15541556
| Parameter | Type | Required | Description |
15551557
| --------- | ---- | -------- | ----------- |
15561558
| `accountId` | string | Yes | The Cloudflare account ID. Workers scripts are account-scoped |
1557-
| `tags` | string | No | Filter scripts by tag, using Cloudflare's tag filter syntax |
1559+
| `tags` | string | No | Filter scripts by tag. Cloudflare expects a comma-separated list of tag:allowed pairs where allowed is yes or no, e.g. team:core:yes,deprecated:no |
15581560
| `apiKey` | string | Yes | Cloudflare API Token |
15591561

15601562
#### Output

0 commit comments

Comments
 (0)