@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gfargo/doorman",
3
- "version": "3.15.0",
3
+ "version": "3.15.2",
4
4
  "description": "Manage firewall rules as code across multiple providers",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -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 15 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` | |
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` | `http.request.headers["key"]` | Requires `key` (the header name) |
74
- | `query` | `http.request.uri.query` | ⚠️ `key` is currently ignored — matches the whole query string, not one parameter ([doorman#263](https://github.com/gfargo/doorman/issues/263)) |
75
- | `cookie` | `http.cookie["key"]` | Requires `key` (the cookie name) |
76
- | `user_agent` | `http.user_agent` | |
77
- | `referer` | `http.referer` | |
78
- | `scheme` | `ssl` (boolean) | |
79
- | `port` | `cf.edge.server_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 **legacy-format-only concepts** — they exist on Vercel's native rule type but have no unified-format equivalent at all, so they're not reachable through a `provider`/`providers`-tagged config for _any_ provider, not just Cloudflare. Every other unified field maps to something on Cloudflare — see Field Mapping above.
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, or the `query`-condition `key`-ignored case noted in Field Mapping above ([doorman#263](https://github.com/gfargo/doorman/issues/263)) — both are known gaps, not something `doorman validate` catches today. Double-check those specifically rather than relying on validation output.
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
- | Type | Description | Example Value |
77
- | -------------------- | -------------------------------- | ----------------------- |
78
- | `path` | URL path | `"/api/users"` |
79
- | `method` | HTTP method | `"POST"` |
80
- | `host` | Hostname | `"example.com"` |
81
- | `user_agent` | User-Agent header | `"Googlebot"` |
82
- | `ip_address` | Client IP | `"192.168.1.1"` |
83
- | `header` | HTTP header (requires `key`) | `"application/json"` |
84
- | `query` | Query parameter (requires `key`) | `"true"` |
85
- | `cookie` | Cookie value (requires `key`) | `"session_abc"` |
86
- | `geo_country` | Country code (ISO 3166-1) | `"US"` or `["US","CA"]` |
87
- | `geo_city` | City name | `"New York"` |
88
- | `geo_continent` | Continent code | `"NA"` |
89
- | `geo_country_region` | Region/state code | `"CA"` |
90
- | `geo_as_number` | ASN number | `13335` |
91
- | `scheme` | URL scheme | `"https"` |
92
- | `protocol` | HTTP protocol version | `"HTTP/2"` |
93
-
94
- **Vercel-only types** (not available on Cloudflare):
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