@gfargo/doorman 3.15.0 → 3.15.2
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.
- package/dist/bin/run.js +46 -46
- package/dist/bin/run.mjs +44 -44
- package/package.json +1 -1
- package/skills/doorman/references/cloudflare.md +27 -21
- package/skills/doorman/references/rules.md +24 -21
package/package.json
CHANGED
|
@@ -58,25 +58,28 @@ Doorman translates its unified rule format (`conditions`/`enabled`/`action: {typ
|
|
|
58
58
|
|
|
59
59
|
### Field Mapping
|
|
60
60
|
|
|
61
|
-
Cloudflare supports all
|
|
62
|
-
|
|
63
|
-
| Doorman Field
|
|
64
|
-
|
|
|
65
|
-
| `ip`
|
|
66
|
-
| `country`
|
|
67
|
-
| `region`
|
|
68
|
-
| `city`
|
|
69
|
-
| `asn`
|
|
70
|
-
| `path`
|
|
71
|
-
| `host`
|
|
72
|
-
| `method`
|
|
73
|
-
| `header`
|
|
74
|
-
| `query`
|
|
75
|
-
| `cookie`
|
|
76
|
-
| `user_agent`
|
|
77
|
-
| `referer`
|
|
78
|
-
| `scheme`
|
|
79
|
-
| `port`
|
|
61
|
+
Cloudflare supports all 16 unified condition fields:
|
|
62
|
+
|
|
63
|
+
| Doorman Field | Cloudflare Field | Notes |
|
|
64
|
+
| -------------- | --------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
|
65
|
+
| `ip` | `ip.src` | |
|
|
66
|
+
| `country` | `ip.geoip.country` | |
|
|
67
|
+
| `region` | `ip.geoip.subdivision_1` | Client geo subdivision (e.g. `"CA"`) — see the legacy-field note below for how this differs from Vercel's own `region` |
|
|
68
|
+
| `city` | `ip.geoip.city` | |
|
|
69
|
+
| `asn` | `ip.geoip.asnum` | |
|
|
70
|
+
| `path` | `http.request.uri.path` | |
|
|
71
|
+
| `host` | `http.host` | |
|
|
72
|
+
| `method` | `http.request.method` | |
|
|
73
|
+
| `header` | `any(http.request.headers["key"][*] op value)` / `has_key(...)` | Requires `key` (the header name, lowercased before compiling) — see the keyed-field note below |
|
|
74
|
+
| `query` | `http.request.uri.query`, or `any(http.request.uri.args["key"][*] op value)` when keyed | Unkeyed matches the whole query string; `key` scopes to one parameter — see the keyed-field note below |
|
|
75
|
+
| `cookie` | `http.cookie`, or `any(http.request.cookies["key"][*] op value)` when keyed | Unkeyed matches the whole `Cookie` header; keyed requires Cloudflare Pro/Business/Enterprise — see the keyed-field note below |
|
|
76
|
+
| `user_agent` | `http.user_agent` | |
|
|
77
|
+
| `referer` | `http.referer` | |
|
|
78
|
+
| `scheme` | `ssl` (boolean) | |
|
|
79
|
+
| `port` | `cf.edge.server_port` | |
|
|
80
|
+
| `threat_score` | `cf.threat_score` | Cloudflare bot/attack threat score (0-100) |
|
|
81
|
+
|
|
82
|
+
**Keyed `header`/`cookie`/`query` conditions** ([doorman#269](https://github.com/gfargo/doorman/issues/269)): `http.request.headers`, `http.request.cookies`, and (when keyed) `http.request.uri.args` all type as Cloudflare's `Map<Array<String>>`, not a scalar `String` — indexing one yields an `Array<String>`, so a bare `field["key"] eq "value"` is an Array-vs-String type mismatch the Cloudflare API rejects. Doorman compiles a keyed condition to the idiom Cloudflare's Ruleset Engine actually documents instead: `any(field["key"][*] <op> value)` for a value comparison, `has_key(field, "key")` for `exists`/`not_exists`. A `header` condition always requires `key` — there's no "all headers as one value" fallback the way `cookie`'s unkeyed `http.cookie` is. `http.request.cookies` requires Cloudflare Pro, Business, or Enterprise; doorman emits it regardless of plan and lets Cloudflare's API reject it on an unsupported plan, the same policy already applied to `matches`/regex below.
|
|
80
83
|
|
|
81
84
|
### Operator Mapping
|
|
82
85
|
|
|
@@ -112,7 +115,10 @@ Without `CLOUDFLARE_ACCOUNT_ID`, IP blocking falls back to individual WAF rules
|
|
|
112
115
|
|
|
113
116
|
## Limitations & Differences
|
|
114
117
|
|
|
115
|
-
`environment`, `ja3_digest`, `ja4_digest` are **
|
|
118
|
+
`protocol`, `target_path`, `environment`, `geo_continent`, `ja3_digest`, `ja4_digest`, `rate_limit_api_id` are **Vercel-native-only concepts** with no Cloudflare equivalent. Unlike what this doc previously claimed, they _are_ reachable through a `provider`/`providers`-tagged config — `UnifiedCondition.field` accepts arbitrary strings, and a Vercel rule using any of them translates straight through into the unified layer unchanged. Cloudflare's translator drops each one from the generated expression with a `TranslationWarning` rather than emitting invalid wirefilter ([doorman#271](https://github.com/gfargo/doorman/issues/271)); if _every_ condition on a rule falls into this set, translation throws rather than syncing a conditionless (match-everything) rule. Two related fields are handled specially, not dropped:
|
|
119
|
+
|
|
120
|
+
- Vercel's own `region` (the edge/deployment location a request was served from, e.g. `"sfo1"`) is namespaced internally to `vercel_region` so it can't collide with the unified `region` field in the table above (client geo subdivision) — before [doorman#270](https://github.com/gfargo/doorman/issues/270) the two silently shared one name, so a Vercel edge-region condition became a Cloudflare `ip.geoip.subdivision_1` check that could never match. `vercel_region` itself is still Cloudflare-unsupported and gets dropped-with-warning like the rest of this list.
|
|
121
|
+
- Vercel's `geo_country_region` ("Region/state code", e.g. `"CA"`) _is_ the same concept as the unified `region` field — it maps there directly and translates to `ip.geoip.subdivision_1` normally, unlike the rest of this list.
|
|
116
122
|
|
|
117
123
|
| Feature | Cloudflare | Notes |
|
|
118
124
|
| ------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------- |
|
|
@@ -130,7 +136,7 @@ The `RuleTranslator` surfaces warnings when a translation is lossy:
|
|
|
130
136
|
- **Unmapped managed-ruleset override action** — an `action`/`overrides[].action` in `managedRules` with no Cloudflare equivalent (only `log`/`deny`/`challenge`/`allow` map cleanly) is dropped with a warning
|
|
131
137
|
- **Negation edge cases** — complex negated conditions may produce subtly different behavior in Wirefilter
|
|
132
138
|
|
|
133
|
-
Doorman does **not** currently warn on the `rate_limit`/`redirect` omitted-config-object cases noted in Action Mapping above
|
|
139
|
+
Doorman does **not** currently warn on the `rate_limit`/`redirect` omitted-config-object cases noted in Action Mapping above — a known gap, not something `doorman validate` catches today. Double-check those specifically rather than relying on validation output.
|
|
134
140
|
|
|
135
141
|
Run `doorman validate` to surface the warnings that do exist before deploying — it auto-detects Cloudflare from the config's `provider` field.
|
|
136
142
|
|
|
@@ -73,30 +73,33 @@ Each condition has:
|
|
|
73
73
|
|
|
74
74
|
### Condition Types
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
|
79
|
-
|
|
|
80
|
-
| `
|
|
81
|
-
| `
|
|
82
|
-
| `
|
|
83
|
-
| `
|
|
84
|
-
| `
|
|
85
|
-
| `
|
|
86
|
-
| `
|
|
87
|
-
| `
|
|
88
|
-
| `
|
|
89
|
-
| `
|
|
90
|
-
| `geo_as_number`
|
|
91
|
-
| `scheme`
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
76
|
+
These translate cleanly to the unified format's condition fields (`mapVercelTypeToUnified` in `src/lib/providers/vercel/translator.ts`), so a rule built from them survives migration to Cloudflare or Fastly:
|
|
77
|
+
|
|
78
|
+
| Type | Description | Example Value |
|
|
79
|
+
| --------------- | -------------------------------- | ----------------------- |
|
|
80
|
+
| `path` | URL path | `"/api/users"` |
|
|
81
|
+
| `method` | HTTP method | `"POST"` |
|
|
82
|
+
| `host` | Hostname | `"example.com"` |
|
|
83
|
+
| `user_agent` | User-Agent header | `"Googlebot"` |
|
|
84
|
+
| `ip_address` | Client IP | `"192.168.1.1"` |
|
|
85
|
+
| `header` | HTTP header (requires `key`) | `"application/json"` |
|
|
86
|
+
| `query` | Query parameter (requires `key`) | `"true"` |
|
|
87
|
+
| `cookie` | Cookie value (requires `key`) | `"session_abc"` |
|
|
88
|
+
| `geo_country` | Country code (ISO 3166-1) | `"US"` or `["US","CA"]` |
|
|
89
|
+
| `geo_city` | City name | `"New York"` |
|
|
90
|
+
| `geo_as_number` | ASN number | `13335` |
|
|
91
|
+
| `scheme` | URL scheme | `"https"` |
|
|
92
|
+
|
|
93
|
+
**Vercel-only types** — these round-trip fine between the legacy shape and the unified format (Vercel stays Vercel), but have no _other_ provider's equivalent: migrating to Fastly drops them with a translation warning, while migrating to Cloudflare currently produces a broken filter expression with no warning at all (`mapUnifiedFieldToCloudflare` in `src/lib/translators/ExpressionBuilder.ts` has no entry for them and falls through to the bare field name, which isn't valid Wirefilter syntax) — avoid these in a rule you intend to sync to Cloudflare:
|
|
94
|
+
|
|
95
|
+
- `geo_continent` — continent code (`"NA"`)
|
|
96
|
+
- `geo_country_region` — region/state code (`"CA"`) — distinct from `region` below, not interchangeable
|
|
97
|
+
- `protocol` — HTTP protocol version (`"HTTP/2"`) — distinct from `scheme` above, not interchangeable
|
|
98
|
+
- `target_path` — URL path, alternate to `path` (no unified mapping despite the similar name)
|
|
99
|
+
- `region` — Vercel edge region (e.g. `"sfo1"`)
|
|
96
100
|
- `environment` — deployment environment (`"production"`, `"preview"`)
|
|
97
101
|
- `ja3_digest` — TLS fingerprint
|
|
98
102
|
- `ja4_digest` — TLS fingerprint v4
|
|
99
|
-
- `region` — Vercel edge region
|
|
100
103
|
- `rate_limit_api_id` — rate limit API identifier
|
|
101
104
|
|
|
102
105
|
### Operators
|