From 17b6d16e2b7d0e8605ae0ed2e704336ef89c2c47 Mon Sep 17 00:00:00 2001 From: Cass Fridkin Date: Thu, 20 Oct 2022 13:01:11 -0500 Subject: [PATCH] begin implementing real real good types for request.cf (#301) Improve the `IncomingRequestCfProperties` type. Previously, this type was based on our docs, which didn't include some fields. Now we've gone through the code that generates these fields and ensured that every property matches up. Additionally, we added examples and documentation for almost everything, so it should be more clear exactly what a certain property is or isn't. --- .changeset/sweet-news-occur.md | 9 + overrides/cf.d.ts | 757 ++++++++++++++++++++++++++++++--- 2 files changed, 711 insertions(+), 55 deletions(-) create mode 100644 .changeset/sweet-news-occur.md diff --git a/.changeset/sweet-news-occur.md b/.changeset/sweet-news-occur.md new file mode 100644 index 00000000..f383e5b0 --- /dev/null +++ b/.changeset/sweet-news-occur.md @@ -0,0 +1,9 @@ +--- +"@cloudflare/workers-types": patch +--- + +Improve the `IncomingRequestCfProperties` type. + +Previously, this type was based on our docs, which didn't include some fields. Now we've gone through the code that generates these fields and ensured that every property matches up. + +Additionally, we added examples and documentation for almost everything, so it should be more clear exactly what a certain property is or isn't. diff --git a/overrides/cf.d.ts b/overrides/cf.d.ts index a1b34a78..8fa85250 100644 --- a/overrides/cf.d.ts +++ b/overrides/cf.d.ts @@ -1,6 +1,13 @@ // TODO: add Response cf object too declare class Request extends Body { + /** + * In addition to the properties on the standard `Request` object, + * the `cf` object contains extra information about the request provided + * by Cloudflare's edge. + * + * Returns undefined when accessed in the playground. + */ readonly cf?: IncomingRequestCfProperties; } @@ -289,97 +296,737 @@ interface RequestInitCfPropertiesImageMinify { } /** - * In addition to the properties on the standard Request object, - * the cf object contains extra information about the request provided - * by Cloudflare's edge. - * - * Note: Currently, settings in the cf object cannot be accessed in the - * playground. + * Request metadata provided by Cloudflare's edge. */ -interface IncomingRequestCfProperties { +type IncomingRequestCfProperties = + IncomingRequestCfPropertiesBase & + IncomingRequestCfPropertiesBotManagementEnterprise & + IncomingRequestCfPropertiesCloudflareForSaaSEnterprise & + IncomingRequestCfPropertiesGeographicInformation & + IncomingRequestCfPropertiesCloudflareAccessOrApiShield; + +interface IncomingRequestCfPropertiesBase { /** - * (e.g. 395747) + * [ASN](https://www.iana.org/assignments/as-numbers/as-numbers.xhtml) of the incoming request. + * + * @example 395747 */ asn: number; + /** - * The organisation which owns the ASN of the incoming request. - * (e.g. Google Cloud) + * The organization which owns the ASN of the incoming request. + * + * @example "Google Cloud" */ asOrganization: string; - botManagement?: IncomingRequestCfPropertiesBotManagement; - city?: string; - clientAcceptEncoding?: string; - clientTcpRtt?: number; - clientTrustScore?: number; + /** - * The three-letter airport code of the data center that the request - * hit. (e.g. "DFW") + * The original value of the `Accept-Encoding` header if Cloudflare modified it. + * + * @example "gzip, deflate, br" */ - colo: string; - continent?: string; + clientAcceptEncoding?: string; + /** - * The two-letter country code in the request. This is the same value - * as that provided in the CF-IPCountry header. (e.g. "US") + * The number of milliseconds it took for the request to reach your worker. + * + * @example 22 */ - country: string; - httpProtocol: string; - isEUCountry?: string; - latitude?: string; - longitude?: string; + clientTcpRtt?: number; + /** - * DMA metro code from which the request was issued, e.g. "635" + * The three-letter [IATA](https://en.wikipedia.org/wiki/IATA_airport_code) + * airport code of the data center that the request hit. + * + * @example "DFW" */ - metroCode?: string; - postalCode?: string; + colo: string; + /** - * e.g. "Texas" + * Represents the upstream's response to a + * [TCP `keepalive` message](https://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html) + * from cloudflare. + * + * For workers with no upstream, this will always be `1`. + * + * @example 3 */ - region?: string; + edgeRequestKeepAliveStatus: IncomingRequestCfPropertiesEdgeRequestKeepAliveStatus; + /** - * e.g. "TX" + * The HTTP Protocol the request used. + * + * @example "HTTP/2" */ - regionCode?: string; + httpProtocol: string; + /** - * e.g. "weight=256;exclusive=1" + * The browser-requested prioritization information in the request object. + * + * If no information was set, defaults to the empty string `""` + * + * @example "weight=192;exclusive=0;group=3;group-weight=127" + * @default "" */ requestPriority: string; + /** - * e.g. "America/Chicago" + * The TLS version of the connection to Cloudflare. + * In requests served over plaintext (without TLS), this property is the empty string `""`. + * + * @example "TLSv1.3" */ - timezone?: string; tlsVersion: string; + + /** + * The cipher for the connection to Cloudflare. + * In requests served over plaintext (without TLS), this property is the empty string `""`. + * + * @example "AEAD-AES128-GCM-SHA256" + */ tlsCipher: string; - tlsClientAuth: IncomingRequestCfPropertiesTLSClientAuth; + + /** + * Metadata containing the [`HELLO`](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.1.2) and [`FINISHED`](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.9) messages from this request's TLS handshake. + * + * If the incoming request was served over plaintext (without TLS) this field is undefined. + */ + tlsExportedAuthenticator?: IncomingRequestCfPropertiesExportedAuthenticatorMetadata; } -interface IncomingRequestCfPropertiesBotManagement { - corporateProxy: boolean; - ja3Hash?: string; +interface IncomingRequestCfPropertiesBotManagementBase { + /** + * Cloudflare’s [level of certainty](https://developers.cloudflare.com/bots/concepts/bot-score/) that a request comes from a bot, + * represented as an integer percentage between `1` (almost certainly human) + * and `99` (almost certainly a bot). + * + * @example 54 + */ score: number; - staticResource: boolean; + + /** + * A boolean value that is true if the request comes from a good bot, like Google or Bing. + * Most customers choose to allow this traffic. For more details, see [Traffic from known bots](https://developers.cloudflare.com/firewall/known-issues-and-faq/#how-does-firewall-rules-handle-traffic-from-known-bots). + */ verifiedBot: boolean; + + /** + * A boolean value that is true if the request originates from a + * Cloudflare-verified proxy service. + */ + corporateProxy: boolean; + + /** + * A boolean value that's true if the request matches [file extensions](https://developers.cloudflare.com/bots/reference/static-resources/) for many types of static resources. + */ + staticResource: boolean; +} + +interface IncomingRequestCfPropertiesBotManagement { + /** + * Results of Cloudflare's Bot Management analysis + */ + botManagement: IncomingRequestCfPropertiesBotManagementBase; + + /** + * Duplicate of `botManagement.score`. + * + * @deprecated + */ + clientTrustScore: number; +} + +interface IncomingRequestCfPropertiesBotManagementEnterprise + extends IncomingRequestCfPropertiesBotManagement { + /** + * Results of Cloudflare's Bot Management analysis + */ + botManagement: IncomingRequestCfPropertiesBotManagementBase & { + /** + * A [JA3 Fingerprint](https://developers.cloudflare.com/bots/concepts/ja3-fingerprint/) to help profile specific SSL/TLS clients + * across different destination IPs, Ports, and X509 certificates. + */ + ja3Hash: string; + }; +} + +interface IncomingRequestCfPropertiesCloudflareForSaaSEnterprise { + /** + * Custom metadata set per-host in [Cloudflare for SaaS](https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/). + * + * This field is only present if you have Cloudflare for SaaS enabled on your account + * and you have followed the [required steps to enable it]((https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/domain-support/custom-metadata/)). + */ + hostMetadata: HostMetadata; } +interface IncomingRequestCfPropertiesCloudflareAccessOrApiShield { + /** + * Information about the client certificate presented to Cloudflare. + * + * This is populated when the incoming request is served over TLS using + * either Cloudflare Access or API Shield (mTLS) + * and the presented SSL certificate has a valid + * [Certificate Serial Number](https://ldapwiki.com/wiki/Certificate%20Serial%20Number) + * (i.e., not `null` or `""`). + * + * Otherwise, a set of placeholder values are used. + * + * The property `certPresented` will be set to `"1"` when + * the object is populated (i.e. the above conditions were met). + */ + tlsClientAuth: + | IncomingRequestCfPropertiesTLSClientAuth + | IncomingRequestCfPropertiesTLSClientAuthPlaceholder; +} + +/** + * Metadata about the request's TLS handshake + */ +interface IncomingRequestCfPropertiesExportedAuthenticatorMetadata { + /** + * The client's [`HELLO` message](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.1.2), encoded in hexadecimal + * + * @example "44372ba35fa1270921d318f34c12f155dc87b682cf36a790cfaa3ba8737a1b5d" + */ + clientHandshake: string; + + /** + * The server's [`HELLO` message](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.1.2), encoded in hexadecimal + * + * @example "44372ba35fa1270921d318f34c12f155dc87b682cf36a790cfaa3ba8737a1b5d" + */ + serverHandshake: string; + + /** + * The client's [`FINISHED` message](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.9), encoded in hexadecimal + * + * @example "084ee802fe1348f688220e2a6040a05b2199a761f33cf753abb1b006792d3f8b" + */ + clientFinished: string; + + /** + * The server's [`FINISHED` message](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.9), encoded in hexadecimal + * + * @example "084ee802fe1348f688220e2a6040a05b2199a761f33cf753abb1b006792d3f8b" + */ + serverFinished: string; +} + +/** + * Geographic data about the request's origin. + */ +type IncomingRequestCfPropertiesGeographicInformation = + | { + /* No geographic data was found for the incoming request. */ + } + | { + /** The country code `"T1"` is used for requests originating on TOR */ + country: "T1"; + } + | { + /** + * The [ISO 3166-1 Alpha 2](https://www.iso.org/iso-3166-country-codes.html) country code the request originated from. + * + * If your worker is [configured to accept TOR connections](https://support.cloudflare.com/hc/en-us/articles/203306930-Understanding-Cloudflare-Tor-support-and-Onion-Routing), this may also be `"T1"`, indicating a request that originated over TOR. + * + * If Cloudflare is unable to determine where the request originated this property is omitted. + * + * @example "GB" + */ + country: Iso3166Alpha2Code; + + /** + * If present, this property indicates that the request originated in the EU + * + * @example "1" + */ + isEUCountry?: "1"; + + /** + * A two-letter code indicating the continent the request originated from. + * + * @example "AN" + */ + continent: ContinentCode; + + /** + * The city the request originated from + * + * @example "Austin" + */ + city?: string; + + /** + * Postal code of the incoming request + * + * @example "78701" + */ + postalCode?: string; + + /** + * Latitude of the incoming request + * + * @example "30.27130" + */ + latitude?: string; + + /** + * Longitude of the incoming request + * + * @example "-97.74260" + */ + longitude?: string; + + /** + * Timezone of the incoming request + * + * @example "America/Chicago" + */ + timezone?: string; + + /** + * If known, the ISO 3166-2 name for the first level region associated with + * the IP address of the incoming request + * + * @example "Texas" + */ + region?: string; + + /** + * If known, the ISO 3166-2 code for the first-level region associated with + * the IP address of the incoming request + * + * @example "TX" + */ + regionCode?: string; + + /** + * Metro code (DMA) of the incoming request + * + * @example "635" + */ + metroCode?: string; + }; + +/** Data about the incoming request's TLS certificate */ interface IncomingRequestCfPropertiesTLSClientAuth { - certIssuerDNLegacy: string; + /** Always `"1"`, indicating that the certificate was presented */ + certPresented: "1"; + + /** + * Result of certificate verification. + * + * @example "FAILED:self signed certificate" + */ + certVerified: Exclude; + + /** The presented certificate's revokation status. + * + * - A value of `"1"` indicates the certificate has been revoked + * - A value of `"0"` indicates the certificate has not been revoked + */ + certRevoked: "1" | "0"; + + /** + * The certificate issuer's [distinguished name](https://knowledge.digicert.com/generalinformation/INFO1745.html) + * + * @example "CN=cloudflareaccess.com, C=US, ST=Texas, L=Austin, O=Cloudflare" + */ certIssuerDN: string; - certIssuerDNRFC2253: string; - certIssuerSKI: string; - certIssuerSerial: string; - certPresented: "0" | "1"; - certSubjectDNLegacy: string; + + /** + * The certificate subject's [distinguished name](https://knowledge.digicert.com/generalinformation/INFO1745.html) + * + * @example "CN=*.cloudflareaccess.com, C=US, ST=Texas, L=Austin, O=Cloudflare" + */ certSubjectDN: string; + + /** + * The certificate issuer's [distinguished name](https://knowledge.digicert.com/generalinformation/INFO1745.html) ([RFC 2253](https://www.rfc-editor.org/rfc/rfc2253.html) formatted) + * + * @example "CN=cloudflareaccess.com, C=US, ST=Texas, L=Austin, O=Cloudflare" + */ + certIssuerDNRFC2253: string; + + /** + * The certificate subject's [distinguished name](https://knowledge.digicert.com/generalinformation/INFO1745.html) ([RFC 2253](https://www.rfc-editor.org/rfc/rfc2253.html) formatted) + * + * @example "CN=*.cloudflareaccess.com, C=US, ST=Texas, L=Austin, O=Cloudflare" + */ certSubjectDNRFC2253: string; - /** In format "Dec 22 19:39:00 2018 GMT" */ - certNotBefore: string; - /** In format "Dec 22 19:39:00 2018 GMT" */ - certNotAfter: string; + + /** The certificate issuer's distinguished name (legacy policies) */ + certIssuerDNLegacy: string; + + /** The certificate subject's distinguished name (legacy policies) */ + certSubjectDNLegacy: string; + + /** + * The certificate's serial number + * + * @example "00936EACBE07F201DF" + */ certSerial: string; - certFingerprintSHA1: string; - /** "SUCCESS", "FAILED:reason", "NONE" */ - certVerified: string; - certRevoked: string; + + /** + * The certificate issuer's serial number + * + * @example "2489002934BDFEA34" + */ + certIssuerSerial: string; + + /** + * The certificate's Subject Key Identifier + * + * @example "BB:AF:7E:02:3D:FA:A6:F1:3C:84:8E:AD:EE:38:98:EC:D9:32:32:D4" + */ certSKI: string; + + /** + * The certificate issuer's Subject Key Identifier + * + * @example "BB:AF:7E:02:3D:FA:A6:F1:3C:84:8E:AD:EE:38:98:EC:D9:32:32:D4" + */ + certIssuerSKI: string; + + /** + * The certificate's SHA-1 fingerprint + * + * @example "6b9109f323999e52259cda7373ff0b4d26bd232e" + */ + certFingerprintSHA1: string; + + /** + * The certificate's SHA-256 fingerprint + * + * @example "acf77cf37b4156a2708e34c4eb755f9b5dbbe5ebb55adfec8f11493438d19e6ad3f157f81fa3b98278453d5652b0c1fd1d71e5695ae4d709803a4d3f39de9dea" + */ + certFingerprintSHA256: string; + + /** + * The effective starting date of the certificate + * + * @example "Dec 22 19:39:00 2018 GMT" + */ + certNotBefore: string; + + /** + * The effective expiration date of the certificate + * + * @example "Dec 22 19:39:00 2018 GMT" + */ + certNotAfter: string; +} + +/** Placeholder values for TLS Client Authorization */ +interface IncomingRequestCfPropertiesTLSClientAuthPlaceholder { + certPresented: "0"; + certVerified: "NONE"; + certRevoked: "0"; + certIssuerDN: ""; + certSubjectDN: ""; + certIssuerDNRFC2253: ""; + certSubjectDNRFC2253: ""; + certIssuerDNLegacy: ""; + certSubjectDNLegacy: ""; + certSerial: ""; + certIssuerSerial: ""; + certSKI: ""; + certIssuerSKI: ""; + certFingerprintSHA1: ""; + certFingerprintSHA256: ""; + certNotBefore: ""; + certNotAfter: ""; } +/** Possible outcomes of TLS verification */ +declare type CertVerificationStatus = + /** Authentication succeeded */ + | "SUCCESS" + + /** No certificate was presented */ + | "NONE" + + /** Failed because the certificate was self-signed */ + | "FAILED:self signed certificate" + + /** Failed because the certificate failed a trust chain check */ + | "FAILED:unable to verify the first certificate" + + /** Failed because the certificate not yet valid */ + | "FAILED:certificate is not yet valid" + + /** Failed because the certificate is expired */ + | "FAILED:certificate has expired" + + /** Failed for another unspecified reason */ + | "FAILED"; + +/** + * An upstream endpoint's response to a TCP `keepalive` message from Cloudflare. + */ +declare type IncomingRequestCfPropertiesEdgeRequestKeepAliveStatus = + | 0 /** Unknown */ + | 1 /** no keepalives (not found) */ + | 2 /** no connection re-use, opening keepalive connection failed */ + | 3 /** no connection re-use, keepalive accepted and saved */ + | 4 /** connection re-use, refused by the origin server (`TCP FIN`) */ + | 5; /** connection re-use, accepted by the origin server */ + +/** ISO 3166-1 Alpha-2 codes */ +declare type Iso3166Alpha2Code = + | "AD" + | "AE" + | "AF" + | "AG" + | "AI" + | "AL" + | "AM" + | "AO" + | "AQ" + | "AR" + | "AS" + | "AT" + | "AU" + | "AW" + | "AX" + | "AZ" + | "BA" + | "BB" + | "BD" + | "BE" + | "BF" + | "BG" + | "BH" + | "BI" + | "BJ" + | "BL" + | "BM" + | "BN" + | "BO" + | "BQ" + | "BR" + | "BS" + | "BT" + | "BV" + | "BW" + | "BY" + | "BZ" + | "CA" + | "CC" + | "CD" + | "CF" + | "CG" + | "CH" + | "CI" + | "CK" + | "CL" + | "CM" + | "CN" + | "CO" + | "CR" + | "CU" + | "CV" + | "CW" + | "CX" + | "CY" + | "CZ" + | "DE" + | "DJ" + | "DK" + | "DM" + | "DO" + | "DZ" + | "EC" + | "EE" + | "EG" + | "EH" + | "ER" + | "ES" + | "ET" + | "FI" + | "FJ" + | "FK" + | "FM" + | "FO" + | "FR" + | "GA" + | "GB" + | "GD" + | "GE" + | "GF" + | "GG" + | "GH" + | "GI" + | "GL" + | "GM" + | "GN" + | "GP" + | "GQ" + | "GR" + | "GS" + | "GT" + | "GU" + | "GW" + | "GY" + | "HK" + | "HM" + | "HN" + | "HR" + | "HT" + | "HU" + | "ID" + | "IE" + | "IL" + | "IM" + | "IN" + | "IO" + | "IQ" + | "IR" + | "IS" + | "IT" + | "JE" + | "JM" + | "JO" + | "JP" + | "KE" + | "KG" + | "KH" + | "KI" + | "KM" + | "KN" + | "KP" + | "KR" + | "KW" + | "KY" + | "KZ" + | "LA" + | "LB" + | "LC" + | "LI" + | "LK" + | "LR" + | "LS" + | "LT" + | "LU" + | "LV" + | "LY" + | "MA" + | "MC" + | "MD" + | "ME" + | "MF" + | "MG" + | "MH" + | "MK" + | "ML" + | "MM" + | "MN" + | "MO" + | "MP" + | "MQ" + | "MR" + | "MS" + | "MT" + | "MU" + | "MV" + | "MW" + | "MX" + | "MY" + | "MZ" + | "NA" + | "NC" + | "NE" + | "NF" + | "NG" + | "NI" + | "NL" + | "NO" + | "NP" + | "NR" + | "NU" + | "NZ" + | "OM" + | "PA" + | "PE" + | "PF" + | "PG" + | "PH" + | "PK" + | "PL" + | "PM" + | "PN" + | "PR" + | "PS" + | "PT" + | "PW" + | "PY" + | "QA" + | "RE" + | "RO" + | "RS" + | "RU" + | "RW" + | "SA" + | "SB" + | "SC" + | "SD" + | "SE" + | "SG" + | "SH" + | "SI" + | "SJ" + | "SK" + | "SL" + | "SM" + | "SN" + | "SO" + | "SR" + | "SS" + | "ST" + | "SV" + | "SX" + | "SY" + | "SZ" + | "TC" + | "TD" + | "TF" + | "TG" + | "TH" + | "TJ" + | "TK" + | "TL" + | "TM" + | "TN" + | "TO" + | "TR" + | "TT" + | "TV" + | "TW" + | "TZ" + | "UA" + | "UG" + | "UM" + | "US" + | "UY" + | "UZ" + | "VA" + | "VC" + | "VE" + | "VG" + | "VI" + | "VN" + | "VU" + | "WF" + | "WS" + | "YE" + | "YT" + | "ZA" + | "ZM" + | "ZW"; + +/** The 2-letter continent codes Cloudflare uses */ +declare type ContinentCode = "AF" | "AN" | "AS" | "EU" | "NA" | "OC" | "SA"; + export {};