diff --git a/etc/fastly_edge_modules/datadome_integration.json b/etc/fastly_edge_modules/datadome_integration.json index d888b211..2f3a6c59 100644 --- a/etc/fastly_edge_modules/datadome_integration.json +++ b/etc/fastly_edge_modules/datadome_integration.json @@ -43,6 +43,20 @@ "type": "string", "description": "Name of a logging endpoint that has been already set up in Fastly.", "required": false + }, { + "label": "Enable referrer restoration", + "name": "restore_referrer", + "type": "boolean", + "description": "True to restore original referrer when a challenge is passed.", + "default": false, + "required": false + }, { + "label": "Enable GraphQL support for POST requests", + "name": "graphql_support", + "type": "boolean", + "description": "True to retrieve GraphQL information and improve detection.", + "default": false, + "required": false } ], "test": { @@ -54,17 +68,17 @@ "vcl": [ { "priority": 7, - "template": "sub set_origin_header {\n if (req.backend.is_origin) {\n if (req.backend == datadome) {\n # Remove all unexpected headers\n header.filter_except(bereq, \"x-datadome-params\", \"accept-charset\", \"accept-language\", \"x-requested-with\", \"x-fl-productid\", \"x-flapi-session-id\", \"fastly-orig-accept-encoding\", \"cache-control\", \"client-id\", \"connection\", \"pragma\", \"accept\", \"headers-list\", \"host\", \"origin\", \"server-hostname\", \"server-name\", \"x-forwarded-for\", \"user-agent\", \"referer\", \"request\", \"content-type\", \"from\", \"true-client-ip\", \"via\", \"x-real-ip\", \"sec-ch-device-memory\", \"sec-ch-ua\", \"sec-ch-ua-arch\", \"sec-ch-ua-full-version-list\", \"sec-ch-ua-mobile\", \"sec-ch-ua-model\", \"sec-ch-ua-platform\", \"sec-fetch-dest\", \"sec-fetch-mode\", \"sec-fetch-site\", \"sec-fetch-user\");\n set bereq.http.x-datadome-params:key = \"{{datadome_api_key}}\";\n set bereq.http.x-datadome-params:requestmodulename = \"FastlyMagento\";\n set bereq.http.x-datadome-params:moduleversion = \"2.19.4\";\n set bereq.http.x-datadome-params:timerequest = time.start.usec;\n set bereq.http.x-datadome-params:servername = server.identity;\n set bereq.http.x-datadome-params:serverregion = server.region;\n set bereq.http.x-datadome-params:ip = urlencode(client.ip);\n set bereq.http.x-forwarded-proto = urlencode(req.protocol);\n set bereq.http.x-datadome-params:authorizationlen = std.strlen(req.http.authorization);\n # Truncating Headers - Start\n set bereq.http.accept-charset = substr(req.http.accept-charset, 0, 128);\n set bereq.http.accept-language = substr(req.http.accept-language, 0, 256);\n set bereq.http.x-requested-with = substr(req.http.x-requested-with, 0, 128);\n set bereq.http.x-fl-productid = substr(req.http.x-fl-productid, 0, 64);\n set bereq.http.x-flapi-session-id = substr(req.http.x-flapi-session-id, 0, 64);\n set bereq.http.fastly-orig-accept-encoding = substr(req.http.fastly-orig-accept-encoding, 0, 128);\n set bereq.http.cache-control = substr(req.http.cache-control, 0, 128);\n set bereq.http.client-id = substr(req.http.client-id, 0, 128);\n set bereq.http.connection = substr(req.http.connection, 0, 128);\n set bereq.http.pragma = substr(req.http.pragma, 0, 128);\n set bereq.http.accept = substr(req.http.accept, 0, 512);\n set bereq.http.headers-list = substr(req.http.headers-list, 0, 512);\n set bereq.http.host = substr(req.http.host, 0, 512);\n set bereq.http.origin = substr(req.http.origin, 0, 512);\n set bereq.http.server-hostname = substr(req.http.server-hostname, 0, 512);\n set bereq.http.server-name = substr(req.http.server-name, 0, 512);\n if( std.strlen(req.http.x-forwarded-for) \u003e 512 ) {\n # Truncate from the end\n set bereq.http.x-forwarded-for = substr(req.http.x-forwarded-for, -512);\n } else {\n set bereq.http.x-forwarded-for = req.http.x-forwarded-for;\n }\n set bereq.http.user-agent = substr(req.http.user-agent, 0, 768);\n set bereq.http.referer = substr(req.http.referer, 0, 1024);\n set bereq.http.request = substr(req.http.request, 0, 2048);\n set bereq.http.content-type = substr(req.http.content-type, 0, 64);\n set bereq.http.from = substr(req.http.from, 0, 128);\n set bereq.http.true-client-ip = substr(req.http.true-client-ip, 0, 128);\n set bereq.http.via = substr(req.http.via, 0, 256);\n set bereq.http.x-real-ip = substr(req.http.x-real-ip, 0, 128);\n set bereq.http.sec-ch-device-memory = substr(req.http.sec-ch-device-memory, 0, 8);\n set bereq.http.sec-ch-ua = substr(req.http.sec-ch-ua, 0, 128);\n set bereq.http.sec-ch-ua-arch = substr(req.http.sec-ch-ua-arch, 0, 16);\n set bereq.http.sec-ch-ua-full-version-list = substr(req.http.sec-ch-ua-full-version-list, 0, 256);\n set bereq.http.sec-ch-ua-mobile = substr(req.http.sec-ch-ua-mobile, 0, 8);\n set bereq.http.sec-ch-ua-model = substr(req.http.sec-ch-ua-model, 0, 128);\n set bereq.http.sec-ch-ua-platform = substr(req.http.sec-ch-ua-platform, 0, 32);\n set bereq.http.sec-fetch-dest = substr(req.http.sec-fetch-dest, 0, 32);\n set bereq.http.sec-fetch-mode = substr(req.http.sec-fetch-mode, 0, 32);\n set bereq.http.sec-fetch-site = substr(req.http.sec-fetch-site, 0, 64);\n set bereq.http.sec-fetch-user = substr(req.http.sec-fetch-user, 0, 8);\n # Truncating Headers - End\n if (req.http.x-datadome-clientid) {\n set bereq.http.x-datadome-params:clientid = urlencode(substr(req.http.x-datadome-clientid, 0, 128));\n set bereq.http.x-datadome-x-set-cookie = \"true\";\n } else {\n set bereq.http.x-datadome-params:clientid = urlencode(substr(req.http.cookie:datadome, 0, 128));\n }\n set bereq.http.x-datadome-params:cookieslen = std.strlen(req.http.cookie);\n # enforce gzip encoding between Fastly and DataDome\n set bereq.http.accept-encoding = \"gzip\";\n } else {\n # prevent leak of the key\n unset bereq.http.x-datadome-params;\n }\n }\n}\n\nbackend datadome {\n .host = \"api-fastly.datadome.co\";\n .port = \"8443\";\n .connect_timeout = {{datadome_connect_timeout}}ms;\n .first_byte_timeout = {{datadome_between_bytes_timeout}}ms;\n .between_bytes_timeout = {{datadome_between_bytes_timeout}}ms;\n .max_connections = 200;\n .ssl = true;\n .dynamic = true;\n .probe = {\n .request = \"HEAD /.well-known/healthcheck-datadome HTTP/1.1\" \"Host: api-fastly.datadome.co\" \"Connection: close\" \"User-Agent: Varnish/fastly (healthcheck)\";\n .expected_response = 200;\n .initial = 5;\n .interval = 2s;\n .threshold = 1;\n .timeout = 2s;\n .window = 5;\n }\n}", + "template": "sub set_origin_header {\n if (req.backend.is_origin) {\n if (req.backend == datadome) {\n # Remove all unexpected headers\n header.filter_except(bereq, \"x-datadome-params\", \"accept-charset\", \"accept-language\", \"x-requested-with\", \"x-fl-productid\", \"x-flapi-session-id\", \"fastly-orig-accept-encoding\", \"cache-control\", \"client-id\", \"connection\", \"pragma\", \"accept\", \"headers-list\", \"host\", \"origin\", \"server-hostname\", \"server-name\", \"x-forwarded-for\", \"user-agent\", \"referer\", \"request\", \"content-type\", \"from\", \"true-client-ip\", \"via\", \"x-real-ip\", \"sec-ch-device-memory\", \"sec-ch-ua\", \"sec-ch-ua-arch\", \"sec-ch-ua-full-version-list\", \"sec-ch-ua-mobile\", \"sec-ch-ua-model\", \"sec-ch-ua-platform\", \"sec-fetch-dest\", \"sec-fetch-mode\", \"sec-fetch-site\", \"sec-fetch-user\");\n set bereq.http.x-datadome-params:key = \"{{datadome_api_key}}\";\n set bereq.http.x-datadome-params:requestmodulename = \"FastlyMagento\";\n set bereq.http.x-datadome-params:moduleversion = \"2.23.0\";\n set bereq.http.x-datadome-params:timerequest = time.start.usec;\n set bereq.http.x-datadome-params:servername = server.identity;\n set bereq.http.x-datadome-params:serverregion = server.region;\n set bereq.http.x-datadome-params:ip = urlencode(client.ip);\n set bereq.http.x-forwarded-proto = urlencode(req.protocol);\n set bereq.http.x-datadome-params:authorizationlen = std.strlen(req.http.authorization);\n # Truncating Headers - Start\n set bereq.http.accept-charset = substr(req.http.accept-charset, 0, 128);\n set bereq.http.accept-language = substr(req.http.accept-language, 0, 256);\n set bereq.http.x-requested-with = substr(req.http.x-requested-with, 0, 128);\n set bereq.http.x-fl-productid = substr(req.http.x-fl-productid, 0, 64);\n set bereq.http.x-flapi-session-id = substr(req.http.x-flapi-session-id, 0, 64);\n set bereq.http.fastly-orig-accept-encoding = substr(req.http.fastly-orig-accept-encoding, 0, 128);\n set bereq.http.cache-control = substr(req.http.cache-control, 0, 128);\n set bereq.http.client-id = substr(req.http.client-id, 0, 128);\n set bereq.http.connection = substr(req.http.connection, 0, 128);\n set bereq.http.pragma = substr(req.http.pragma, 0, 128);\n set bereq.http.accept = substr(req.http.accept, 0, 512);\n set bereq.http.headers-list = substr(req.http.headers-list, 0, 512);\n set bereq.http.host = substr(req.http.host, 0, 512);\n set bereq.http.origin = substr(req.http.origin, 0, 512);\n set bereq.http.server-hostname = substr(req.http.server-hostname, 0, 512);\n set bereq.http.server-name = substr(req.http.server-name, 0, 512);\n if( std.strlen(req.http.x-forwarded-for) \u003e 512 ) {\n # Truncate from the end\n set bereq.http.x-forwarded-for = substr(req.http.x-forwarded-for, -512);\n } else {\n set bereq.http.x-forwarded-for = req.http.x-forwarded-for;\n }\n set bereq.http.user-agent = substr(req.http.user-agent, 0, 768);\n set bereq.http.referer = substr(req.http.referer, 0, 1024);\n set bereq.http.request = substr(req.http.request, 0, 2048);\n set bereq.http.content-type = substr(req.http.content-type, 0, 64);\n set bereq.http.from = substr(req.http.from, 0, 128);\n set bereq.http.true-client-ip = substr(req.http.true-client-ip, 0, 128);\n set bereq.http.via = substr(req.http.via, 0, 256);\n set bereq.http.x-real-ip = substr(req.http.x-real-ip, 0, 128);\n set bereq.http.sec-ch-device-memory = substr(req.http.sec-ch-device-memory, 0, 8);\n set bereq.http.sec-ch-ua = substr(req.http.sec-ch-ua, 0, 128);\n set bereq.http.sec-ch-ua-arch = substr(req.http.sec-ch-ua-arch, 0, 16);\n set bereq.http.sec-ch-ua-full-version-list = substr(req.http.sec-ch-ua-full-version-list, 0, 256);\n set bereq.http.sec-ch-ua-mobile = substr(req.http.sec-ch-ua-mobile, 0, 8);\n set bereq.http.sec-ch-ua-model = substr(req.http.sec-ch-ua-model, 0, 128);\n set bereq.http.sec-ch-ua-platform = substr(req.http.sec-ch-ua-platform, 0, 32);\n set bereq.http.sec-fetch-dest = substr(req.http.sec-fetch-dest, 0, 32);\n set bereq.http.sec-fetch-mode = substr(req.http.sec-fetch-mode, 0, 32);\n set bereq.http.sec-fetch-site = substr(req.http.sec-fetch-site, 0, 64);\n set bereq.http.sec-fetch-user = substr(req.http.sec-fetch-user, 0, 8);\n # Truncating Headers - End\n if (req.http.x-datadome-clientid) {\n set bereq.http.x-datadome-params:clientid = urlencode(substr(req.http.x-datadome-clientid, 0, 128));\n set bereq.http.x-datadome-x-set-cookie = \"true\";\n } else {\n set bereq.http.x-datadome-params:clientid = urlencode(substr(req.http.cookie:datadome, 0, 128));\n }\n set bereq.http.x-datadome-params:cookieslen = std.strlen(req.http.cookie);\n # enforce gzip encoding between Fastly and DataDome\n set bereq.http.accept-encoding = \"gzip\";\n # disable ng-waf inspection on DataDome requests\n set bereq.http.x-sigsci-no-inspection = \"true\";\n } else {\n # prevent leak of the key\n unset bereq.http.x-datadome-params;\n }\n }\n}\n\nbackend datadome {\n .host = \"api-fastly.datadome.co\";\n .port = \"8443\";\n .max_tls_version = \"1.3\";\n .min_tls_version = \"1.2\";\n .connect_timeout = {{datadome_connect_timeout}}ms;\n .first_byte_timeout = {{datadome_between_bytes_timeout}}ms;\n .between_bytes_timeout = {{datadome_between_bytes_timeout}}ms;\n .max_connections = 200;\n .ssl = true;\n .dynamic = true;\n .probe = {\n .request = \"HEAD /.well-known/healthcheck-datadome HTTP/1.1\" \"Host: api-fastly.datadome.co\" \"Connection: close\" \"User-Agent: Varnish/fastly (healthcheck)\";\n .expected_response = 200;\n .initial = 5;\n .interval = 2s;\n .threshold = 1;\n .timeout = 2s;\n .window = 5;\n }\n}", "type": "init" }, { "priority": 7, - "template": "if (req.backend == datadome) {\n declare local var.status STRING;\n set var.status = beresp.status;\n # check that it is real ApiServer response\n if (var.status != beresp.http.x-datadomeresponse) {\n restart;\n }\n unset beresp.http.x-datadomeresponse;\n # copy datadome headers\n set req.http.x-datadome-headers-pairs:x-datadome-headers = urlencode(beresp.http.x-datadome-headers);\n\n if (beresp.http.x-datadome-headers ~ \"(?i)(^| )+x-set-cookie( |$)+\") {\n set req.http.x-datadome-headers-pairs:x-set-cookie = urlencode(beresp.http.x-set-cookie);\n }\n if (beresp.http.x-datadome-headers ~ \"(?i)(^| )+x-datadome-server( |$)+\") {\n set req.http.x-datadome-headers-pairs:x-datadome-server = urlencode(beresp.http.x-datadome-server);\n }\n if (beresp.http.x-datadome-headers ~ \"(?i)(^| )+x-datadome( |$)+\") {\n set req.http.x-datadome-headers-pairs:x-datadome = urlencode(beresp.http.x-datadome);\n }\n if (beresp.http.x-datadome-headers ~ \"(?i)(^| )+content-type( |$)+\") {\n set req.http.x-datadome-headers-pairs:content-type = urlencode(beresp.http.content-type);\n }\n if (beresp.http.x-datadome-headers ~ \"(?i)(^| )+charset( |$)+\") {\n set req.http.x-datadome-headers-pairs:charset = urlencode(beresp.http.charset);\n }\n if (beresp.http.x-datadome-headers ~ \"(?i)(^| )+cache-control( |$)+\") {\n set req.http.x-datadome-headers-pairs:cache-control = urlencode(beresp.http.cache-control);\n }\n if (beresp.http.x-datadome-headers ~ \"(?i)(^| )+pragma( |$)+\") {\n set req.http.x-datadome-headers-pairs:pragma = urlencode(beresp.http.pragma);\n }\n if (beresp.http.x-datadome-headers ~ \"(?i)(^| )+access-control-allow-credentials( |$)+\") {\n set req.http.x-datadome-headers-pairs:access-control-allow-credentials = urlencode(beresp.http.access-control-allow-credentials);\n }\n if (beresp.http.x-datadome-headers ~ \"(?i)(^| )+access-control-expose-headers( |$)+\") {\n set req.http.x-datadome-headers-pairs:access-control-expose-headers = urlencode(beresp.http.access-control-expose-headers);\n }\n if (beresp.http.x-datadome-headers ~ \"(?i)(^| )+access-control-allow-origin( |$)+\") {\n set req.http.x-datadome-headers-pairs:access-control-allow-origin = urlencode(beresp.http.access-control-allow-origin);\n }\n if (beresp.http.x-datadome-headers ~ \"(?i)(^| )+x-datadome-cid( |$)+\") {\n set req.http.x-datadome-headers-pairs:x-datadome-cid = urlencode(beresp.http.x-datadome-cid);\n }\n if (beresp.http.x-datadome-headers ~ \"(?i)(^| )+x-dd-b( |$)+\") {\n set req.http.x-datadome-headers-pairs:x-dd-b = urlencode(beresp.http.x-dd-b);\n }\n if (beresp.http.x-datadome-headers ~ \"(?i)(^| )+x-dd-type( |$)+\") {\n set req.http.x-datadome-headers-pairs:x-dd-type = urlencode(beresp.http.x-dd-type);\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-dd-type( |$)+\") {\n set req.http.x-dd-type = beresp.http.x-dd-type;\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-botname( |$)+\") {\n set req.http.x-datadome-botname = beresp.http.x-datadome-botname;\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-botfamily( |$)+\") {\n set req.http.x-datadome-botfamily = beresp.http.x-datadome-botfamily;\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-isbot( |$)+\") {\n set req.http.x-datadome-isbot = beresp.http.x-datadome-isbot;\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-captchapassed( |$)+\") {\n set req.http.x-datadome-captchapassed = beresp.http.x-datadome-captchapassed;\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-devicecheckpassed( |$)+\") {\n set req.http.x-datadome-devicecheckpassed = beresp.http.x-datadome-devicecheckpassed;\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-traffic-rule-response( |$)+\") {\n set req.http.x-datadome-traffic-rule-response = beresp.http.x-datadome-traffic-rule-response;\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-captchaurl( |$)+\") {\n set req.http.x-datadome-captchaurl = beresp.http.x-datadome-captchaurl;\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-requestid( |$)+\") {\n set req.http.x-datadome-requestid = beresp.http.x-datadome-requestid;\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-score( |$)+\") {\n set req.http.x-datadome-score = beresp.http.x-datadome-score;\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-ruletype( |$)+\") {\n set req.http.x-datadome-ruletype = beresp.http.x-datadome-ruletype;\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-matchedmodels( |$)+\") {\n set req.http.x-datadome-matchedmodels = beresp.http.x-datadome-matchedmodels;\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-sessionid( |$)+\") {\n set req.http.x-datadome-sessionid = beresp.http.x-datadome-sessionid;\n }\n # don\u0027t forget about ApiServer\u0027s cookies\n if (beresp.http.x-datadome-headers ~ \"(?i)(^| )+set-cookie( |$)+\") {\n set req.http.x-datadome-headers-pairs:set-cookie = urlencode(beresp.http.set-cookie);\n }\n\n # Continue only if ApiServer returns expected blocked status\n if (beresp.status != 403 \u0026\u0026 beresp.status != 401 \u0026\u0026 beresp.status != 301 \u0026\u0026 beresp.status != 302) {\n unset beresp.http.x-datadome-headers;\n unset beresp.http.x-datadome-request-headers;\n set req.http.x-datadome-cookie = beresp.http.x-datadome-cookie; # Allow Session Feature\n restart;\n }\n\n # ok, it is banned request, cleanup it a bit\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-dd-type( |$)+\") {\n if (beresp.http.x-datadome-headers !~ \"(?i)(^| )+x-dd-type( |$)+\") {\n unset beresp.http.x-dd-type;\n }\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-botname( |$)+\") {\n if (beresp.http.x-datadome-headers !~ \"(?i)(^| )+x-datadome-botname( |$)+\") {\n unset beresp.http.x-datadome-botname;\n }\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-botfamily( |$)+\") {\n if (beresp.http.x-datadome-headers !~ \"(?i)(^| )+x-datadome-botfamily( |$)+\") {\n unset beresp.http.x-datadome-botfamily;\n }\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-isbot( |$)+\") {\n if (beresp.http.x-datadome-headers !~ \"(?i)(^| )+x-datadome-isbot( |$)+\") {\n unset beresp.http.x-datadome-isbot;\n }\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-captchapassed( |$)+\") {\n if (beresp.http.x-datadome-headers !~ \"(?i)(^| )+x-datadome-captchapassed( |$)+\") {\n unset beresp.http.x-datadome-captchapassed;\n }\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-devicecheckpassed( |$)+\") {\n if (beresp.http.x-datadome-headers !~ \"(?i)(^| )+x-datadome-devicecheckpassed( |$)+\") {\n unset beresp.http.x-datadome-devicecheckpassed;\n }\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-traffic-rule-response( |$)+\") {\n if (beresp.http.x-datadome-headers !~ \"(?i)(^| )+x-datadome-traffic-rule-response( |$)+\") {\n unset beresp.http.x-datadome-traffic-rule-response;\n }\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-captchaurl( |$)+\") {\n if (beresp.http.x-datadome-headers !~ \"(?i)(^| )+x-datadome-captchaurl( |$)+\") {\n unset beresp.http.x-datadome-captchaurl;\n }\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-requestid( |$)+\") {\n if (beresp.http.x-datadome-headers !~ \"(?i)(^| )+x-datadome-requestid( |$)+\") {\n unset beresp.http.x-datadome-requestid;\n }\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-score( |$)+\") {\n if (beresp.http.x-datadome-headers !~ \"(?i)(^| )+x-datadome-score( |$)+\") {\n unset beresp.http.x-datadome-score;\n }\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-ruletype( |$)+\") {\n if (beresp.http.x-datadome-headers !~ \"(?i)(^| )+x-datadome-ruletype( |$)+\") {\n unset beresp.http.x-datadome-ruletype;\n }\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-matchedmodels( |$)+\") {\n if (beresp.http.x-datadome-headers !~ \"(?i)(^| )+x-datadome-matchedmodels( |$)+\") {\n unset beresp.http.x-datadome-matchedmodels;\n }\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-sessionid( |$)+\") {\n if (beresp.http.x-datadome-headers !~ \"(?i)(^| )+x-datadome-sessionid( |$)+\") {\n unset beresp.http.x-datadome-sessionid;\n }\n }\n unset beresp.http.x-datadome-headers;\n unset beresp.http.x-datadome-request-headers;\n}", + "template": "if (req.backend == datadome) {\n declare local var.status STRING;\n set var.status = beresp.status;\n # check that it is real ApiServer response\n if (var.status != beresp.http.x-datadomeresponse) {\n restart;\n }\n unset beresp.http.x-datadomeresponse;\n # copy datadome headers\n set req.http.x-datadome-headers-pairs:x-datadome-headers = urlencode(beresp.http.x-datadome-headers);\n\n if (beresp.http.x-datadome-headers ~ \"(?i)(^| )+x-set-cookie( |$)+\") {\n set req.http.x-datadome-headers-pairs:x-set-cookie = urlencode(beresp.http.x-set-cookie);\n }\n if (beresp.http.x-datadome-headers ~ \"(?i)(^| )+x-datadome-server( |$)+\") {\n set req.http.x-datadome-headers-pairs:x-datadome-server = urlencode(beresp.http.x-datadome-server);\n }\n if (beresp.http.x-datadome-headers ~ \"(?i)(^| )+x-datadome( |$)+\") {\n set req.http.x-datadome-headers-pairs:x-datadome = urlencode(beresp.http.x-datadome);\n }\n if (beresp.http.x-datadome-headers ~ \"(?i)(^| )+content-type( |$)+\") {\n set req.http.x-datadome-headers-pairs:content-type = urlencode(beresp.http.content-type);\n }\n if (beresp.http.x-datadome-headers ~ \"(?i)(^| )+charset( |$)+\") {\n set req.http.x-datadome-headers-pairs:charset = urlencode(beresp.http.charset);\n }\n if (beresp.http.x-datadome-headers ~ \"(?i)(^| )+cache-control( |$)+\") {\n set req.http.x-datadome-headers-pairs:cache-control = urlencode(beresp.http.cache-control);\n }\n if (beresp.http.x-datadome-headers ~ \"(?i)(^| )+pragma( |$)+\") {\n set req.http.x-datadome-headers-pairs:pragma = urlencode(beresp.http.pragma);\n }\n if (beresp.http.x-datadome-headers ~ \"(?i)(^| )+access-control-allow-credentials( |$)+\") {\n set req.http.x-datadome-headers-pairs:access-control-allow-credentials = urlencode(beresp.http.access-control-allow-credentials);\n }\n if (beresp.http.x-datadome-headers ~ \"(?i)(^| )+access-control-expose-headers( |$)+\") {\n set req.http.x-datadome-headers-pairs:access-control-expose-headers = urlencode(beresp.http.access-control-expose-headers);\n }\n if (beresp.http.x-datadome-headers ~ \"(?i)(^| )+access-control-allow-origin( |$)+\") {\n set req.http.x-datadome-headers-pairs:access-control-allow-origin = urlencode(beresp.http.access-control-allow-origin);\n }\n if (beresp.http.x-datadome-headers ~ \"(?i)(^| )+x-datadome-cid( |$)+\") {\n set req.http.x-datadome-headers-pairs:x-datadome-cid = urlencode(beresp.http.x-datadome-cid);\n }\n if (beresp.http.x-datadome-headers ~ \"(?i)(^| )+x-dd-b( |$)+\") {\n set req.http.x-datadome-headers-pairs:x-dd-b = urlencode(beresp.http.x-dd-b);\n }\n if (beresp.http.x-datadome-headers ~ \"(?i)(^| )+x-dd-type( |$)+\") {\n set req.http.x-datadome-headers-pairs:x-dd-type = urlencode(beresp.http.x-dd-type);\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-dd-type( |$)+\") {\n set req.http.x-dd-type = beresp.http.x-dd-type;\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-botname( |$)+\") {\n set req.http.x-datadome-botname = beresp.http.x-datadome-botname;\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-botfamily( |$)+\") {\n set req.http.x-datadome-botfamily = beresp.http.x-datadome-botfamily;\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-isbot( |$)+\") {\n set req.http.x-datadome-isbot = beresp.http.x-datadome-isbot;\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-captchapassed( |$)+\") {\n set req.http.x-datadome-captchapassed = beresp.http.x-datadome-captchapassed;\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-devicecheckpassed( |$)+\") {\n set req.http.x-datadome-devicecheckpassed = beresp.http.x-datadome-devicecheckpassed;\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-traffic-rule-response( |$)+\") {\n set req.http.x-datadome-traffic-rule-response = beresp.http.x-datadome-traffic-rule-response;\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-captchaurl( |$)+\") {\n set req.http.x-datadome-captchaurl = beresp.http.x-datadome-captchaurl;\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-requestid( |$)+\") {\n set req.http.x-datadome-requestid = beresp.http.x-datadome-requestid;\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-score( |$)+\") {\n set req.http.x-datadome-score = beresp.http.x-datadome-score;\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-ruletype( |$)+\") {\n set req.http.x-datadome-ruletype = beresp.http.x-datadome-ruletype;\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-matchedmodels( |$)+\") {\n set req.http.x-datadome-matchedmodels = beresp.http.x-datadome-matchedmodels;\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-sessionid( |$)+\") {\n set req.http.x-datadome-sessionid = beresp.http.x-datadome-sessionid;\n }\n # don\u0027t forget about ApiServer\u0027s cookies\n if (beresp.http.x-datadome-headers ~ \"(?i)(^| )+set-cookie( |$)+\") {\n set req.http.x-datadome-headers-pairs:set-cookie = urlencode(beresp.http.set-cookie);\n }\n\n # Continue only if ApiServer returns expected blocked status\n if (beresp.status != 403 \u0026\u0026 beresp.status != 401 \u0026\u0026 beresp.status != 301 \u0026\u0026 beresp.status != 302) {\n unset beresp.http.x-datadome-headers;\n unset beresp.http.x-datadome-request-headers;\n set req.http.x-datadome-cookie = beresp.http.x-datadome-cookie; # Allow Session Feature\n restart;\n }\n\n # ok, it is banned request, cleanup it a bit\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-dd-type( |$)+\") {\n if (beresp.http.x-datadome-headers !~ \"(?i)(^| )+x-dd-type( |$)+\") {\n unset beresp.http.x-dd-type;\n }\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-botname( |$)+\") {\n if (beresp.http.x-datadome-headers !~ \"(?i)(^| )+x-datadome-botname( |$)+\") {\n unset beresp.http.x-datadome-botname;\n }\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-botfamily( |$)+\") {\n if (beresp.http.x-datadome-headers !~ \"(?i)(^| )+x-datadome-botfamily( |$)+\") {\n unset beresp.http.x-datadome-botfamily;\n }\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-isbot( |$)+\") {\n if (beresp.http.x-datadome-headers !~ \"(?i)(^| )+x-datadome-isbot( |$)+\") {\n unset beresp.http.x-datadome-isbot;\n }\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-captchapassed( |$)+\") {\n if (beresp.http.x-datadome-headers !~ \"(?i)(^| )+x-datadome-captchapassed( |$)+\") {\n unset beresp.http.x-datadome-captchapassed;\n }\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-devicecheckpassed( |$)+\") {\n if (beresp.http.x-datadome-headers !~ \"(?i)(^| )+x-datadome-devicecheckpassed( |$)+\") {\n unset beresp.http.x-datadome-devicecheckpassed;\n }\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-traffic-rule-response( |$)+\") {\n if (beresp.http.x-datadome-headers !~ \"(?i)(^| )+x-datadome-traffic-rule-response( |$)+\") {\n unset beresp.http.x-datadome-traffic-rule-response;\n }\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-captchaurl( |$)+\") {\n if (beresp.http.x-datadome-headers !~ \"(?i)(^| )+x-datadome-captchaurl( |$)+\") {\n unset beresp.http.x-datadome-captchaurl;\n }\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-requestid( |$)+\") {\n if (beresp.http.x-datadome-headers !~ \"(?i)(^| )+x-datadome-requestid( |$)+\") {\n unset beresp.http.x-datadome-requestid;\n }\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-score( |$)+\") {\n if (beresp.http.x-datadome-headers !~ \"(?i)(^| )+x-datadome-score( |$)+\") {\n unset beresp.http.x-datadome-score;\n }\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-ruletype( |$)+\") {\n if (beresp.http.x-datadome-headers !~ \"(?i)(^| )+x-datadome-ruletype( |$)+\") {\n unset beresp.http.x-datadome-ruletype;\n }\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-matchedmodels( |$)+\") {\n if (beresp.http.x-datadome-headers !~ \"(?i)(^| )+x-datadome-matchedmodels( |$)+\") {\n unset beresp.http.x-datadome-matchedmodels;\n }\n }\n if (beresp.http.x-datadome-request-headers ~ \"(?i)(^| )+x-datadome-sessionid( |$)+\") {\n if (beresp.http.x-datadome-headers !~ \"(?i)(^| )+x-datadome-sessionid( |$)+\") {\n unset beresp.http.x-datadome-sessionid;\n }\n }\n unset beresp.http.x-datadome-headers;\n unset beresp.http.x-datadome-request-headers;\n # restore method\n if (req.http.x-datadome-params:method) {\n set req.method = urldecode(req.http.x-datadome-params:method);\n }\n}", "type": "fetch" }, { "priority": 7, - "template": "if (req.backend == datadome) {\n restart;\n}", + "template": "if (req.backend == datadome \u0026\u0026 req.http.x-datadome-params) {\n # the backend is set to \u0027DataDome\u0027 and\n # the presence of a header confirms it\u0027s an error during DataDome process\n restart;\n}", "type": "error" }, { @@ -74,7 +88,7 @@ }, { "priority": 7, - "template": "\n {{#if logging_endpoint}}\n ## Debug DataDome\n log {\"syslog \"} req.service_id {\" {{logging_endpoint}} :: \"}\n \" timestamp=%22\" now\n \"%22 client_ip=\" req.http.Fastly-Client-IP\n \" request=\" req.method\n \" url=%22\" cstr_escape(req.url.path)\n \"%22 restarts=\" req.restarts\n \" DataDomeDebug=\" \"Before_DataDome\"\n \" fastlyFF=\" fastly.ff.visits_this_service;\n ##\n {{/if}}# Configure the regular expression below to match URLs that\n# should be checked by DataDome\nif (fastly.ff.visits_this_service == 0 \u0026\u0026 req.restarts == 0 \u0026\u0026 req.method != \"FASTLYPURGE\" \u0026\u0026 !(req.url.path ~ \"{{datadome_exclusion_ext}}\" \u0026\u0026 (req.method == \"GET\" || req.method == \"HEAD\"))) {\n\n set req.backend = datadome;\n unset req.http.x-datadome-params;\n # Configure the string below to include your DataDome API key\n set req.http.x-datadome-params:method = urlencode(req.method);\n set req.http.x-datadome-params:postparamlen = urlencode(req.http.content-length);\n set req.method = \"GET\";\n set req.http.x-datadome-params:tlsprotocol = urlencode(tls.client.protocol);\n set req.http.x-datadome-params:tlscipherslist = urlencode(tls.client.ciphers_list);\n set req.http.x-datadome-params:tlsextensionslist = urlencode(tls.client.tlsexts_list);\n set req.http.x-datadome-params:ja3 = urlencode(tls.client.ja3_md5);\n {{#if logging_endpoint}}\n ## Debug DataDome\n log {\"syslog \"} req.service_id {\" {{logging_endpoint}} :: \"}\n \" timestamp=%22\" now\n \"%22 client_ip=\" req.http.Fastly-Client-IP\n \" request=\" req.method\n \" host=\" req.http.host\n \" url=%22\" cstr_escape(req.url)\n \"%22 request_referer=%22\" cstr_escape(req.http.Referer)\n \"%22 request_user_agent=%22\" cstr_escape(req.http.User-Agent)\n \"%22 request_accept_language=%22\" cstr_escape(req.http.Accept-Language)\n \"%22 request_accept_charset=%22\" cstr_escape(req.http.Accept-Charset)\n \"%22 contentLength=\" req.http.Content-Length\n \" restarts=\" req.restarts\n \" DataDomeDebug=\" \"To_DataDome\"\n \" fastlyFF=\" fastly.ff.visits_this_service;\n ##\n {{/if}}\n return (pass);\n} else {\n if (req.http.x-datadome-params:method) {\n set req.method = urldecode(req.http.x-datadome-params:method);\n # After a restart, clustering is disabled. This re-enables it.\n set req.http.fastly-force-shield = \"1\";\n }\n unset req.http.x-datadome-params;\n {{#if logging_endpoint}}\n ## Debug DataDome\n log {\"syslog \"} req.service_id {\" {{logging_endpoint}} :: \"}\n \" timestamp=%22\" now\n \"%22 client_ip=\" req.http.Fastly-Client-IP\n \" request=\" req.method\n \" host=\" req.http.host\n \" url=%22\" cstr_escape(req.url)\n \"%22 request_referer=%22\" cstr_escape(req.http.Referer)\n \"%22 request_user_agent=%22\" cstr_escape(req.http.User-Agent)\n \"%22 request_accept_language=%22\" cstr_escape(req.http.Accept-Language)\n \"%22 request_accept_charset=%22\" cstr_escape(req.http.Accept-Charset)\n \"%22 contentLength=\" req.http.Content-Length\n \" restarts=\" req.restarts\n \" DataDomeDebug=\" \"Bypass_DataDome\"\n \" fastlyFF=\" fastly.ff.visits_this_service;\n ##\n {{/if}}\n}\n\n# we\u0027re using the first restart for datadome, update a part of fastly code\n# we can\u0027t replace whole macros because we haven\u0027t got any idea about backends\nif (req.restarts == 1) {\n if (!req.http.x-timer) {\n set req.http.x-timer = \"S\" time.start.sec \".\" time.start.usec_frac;\n }\n set req.http.x-timer = req.http.x-timer \",VS0\";\n}\n\nset var.fastly_req_do_shield = (req.restarts \u003c= 1);", + "template": "\n {{#if logging_endpoint}}\n ## Debug DataDome\n log {\"syslog \"} req.service_id {\" {{logging_endpoint}} :: \"}\n \" timestamp=%22\" now\n \"%22 client_ip=\" req.http.Fastly-Client-IP\n \" request=\" req.method\n \" url=%22\" cstr_escape(req.url.path)\n \"%22 restarts=\" req.restarts\n \" DataDomeDebug=\" \"Before_DataDome\"\n \" fastlyFF=\" fastly.ff.visits_this_service;\n ##\n {{/if}}# Configure the regular expression below to match URLs that\n# should be checked by DataDome\nif (fastly.ff.visits_this_service == 0 \u0026\u0026 req.restarts == 0 \u0026\u0026 req.method != \"FASTLYPURGE\" \u0026\u0026 !(req.url.path ~ \"{{datadome_exclusion_ext}}\" \u0026\u0026 (req.method == \"GET\" || req.method == \"HEAD\"))) {\n\n set req.backend = datadome;\n unset req.http.x-datadome-params;\n set req.http.x-datadome-params:method = urlencode(req.method);\n set req.http.x-datadome-params:postparamlen = urlencode(req.http.content-length);\n set req.method = \"GET\";\n set req.http.x-datadome-params:tlsprotocol = urlencode(tls.client.protocol);\n set req.http.x-datadome-params:tlscipherslist = urlencode(tls.client.ciphers_list);\n set req.http.x-datadome-params:tlsextensionslist = urlencode(tls.client.tlsexts_list);\n set req.http.x-datadome-params:ja3 = urlencode(tls.client.ja3_md5);\n set req.http.x-datadome-params:ja4 = urlencode(tls.client.ja4);\n\n if (var.datadome_enable_graphql_support == true \u0026\u0026 req.http.x-datadome-params:method == \"POST\" \u0026\u0026 req.http.content-type ~ \"(?i)application\\/json\" \u0026\u0026 req.url.path ~ \"(?i)graphql\") {\n declare local var.graphQLQuery STRING;\n set var.graphQLQuery = std.strstr(req.body, \"%22query%22\");\n if (var.graphQLQuery ~ \"(?m)(query|mutation|subscription)?\\s*([A-Za-z_][A-Za-z0-9_]*)?\\s*[\\({@]\") {\n set req.http.x-datadome-params:graphqlOperationType = if(re.group.1, re.group.1, \"query\");\n if (re.group.2) {\n set req.http.x-datadome-params:graphqlOperationName = substr(re.group.2, 0, 128);\n }\n }\n }\n\n if (var.datadome_restore_referrer == true \u0026\u0026 req.url ~ \"dd_referrer=\") {\n \n declare local var.decoded_url STRING;\n declare local var.complete_request_url STRING;\n set var.decoded_url = urldecode(req.url);\n set var.complete_request_url = req.protocol \"://\" req.http.host querystring.filter(var.decoded_url, \"dd_referrer\");\n \n if (urldecode(querystring.clean(req.http.referer)) == querystring.clean(var.complete_request_url)) {\n # Referer header is the same as the current URL\n\n if (std.strlen(querystring.get(req.url, \"dd_referrer\")) \u003e 0) {\n # Not empty `dd_referrer`, restore header `referer` with param value\n set req.http.referer = urldecode(querystring.get(req.url, \"dd_referrer\"));\n } else if (querystring.get(req.url, \"dd_referrer\") == \"\") {\n # Empty `dd_referrer` param, remove header `referer`\n unset req.http.referer;\n } # Not set `dd_referrer`, do nothing\n \n # Remove `dd_referrer` query param from URL\n set req.url = querystring.filter(req.url, \"dd_referrer\");\n }\n }\n {{#if logging_endpoint}}\n ## Debug DataDome\n log {\"syslog \"} req.service_id {\" {{logging_endpoint}} :: \"}\n \" timestamp=%22\" now\n \"%22 client_ip=\" req.http.Fastly-Client-IP\n \" request=\" req.method\n \" host=\" req.http.host\n \" url=%22\" cstr_escape(req.url)\n \"%22 request_referer=%22\" cstr_escape(req.http.Referer)\n \"%22 request_user_agent=%22\" cstr_escape(req.http.User-Agent)\n \"%22 request_accept_language=%22\" cstr_escape(req.http.Accept-Language)\n \"%22 request_accept_charset=%22\" cstr_escape(req.http.Accept-Charset)\n \"%22 contentLength=\" req.http.Content-Length\n \" restarts=\" req.restarts\n \" DataDomeDebug=\" \"To_DataDome\"\n \" fastlyFF=\" fastly.ff.visits_this_service;\n ##\n {{/if}}\n return (pass);\n} else {\n if (req.http.x-datadome-params:method) {\n set req.method = urldecode(req.http.x-datadome-params:method);\n # After a restart, clustering is disabled. This re-enables it.\n set req.http.fastly-force-shield = \"1\";\n }\n unset req.http.x-datadome-params;\n {{#if logging_endpoint}}\n ## Debug DataDome\n log {\"syslog \"} req.service_id {\" {{logging_endpoint}} :: \"}\n \" timestamp=%22\" now\n \"%22 client_ip=\" req.http.Fastly-Client-IP\n \" request=\" req.method\n \" host=\" req.http.host\n \" url=%22\" cstr_escape(req.url)\n \"%22 request_referer=%22\" cstr_escape(req.http.Referer)\n \"%22 request_user_agent=%22\" cstr_escape(req.http.User-Agent)\n \"%22 request_accept_language=%22\" cstr_escape(req.http.Accept-Language)\n \"%22 request_accept_charset=%22\" cstr_escape(req.http.Accept-Charset)\n \"%22 contentLength=\" req.http.Content-Length\n \" restarts=\" req.restarts\n \" DataDomeDebug=\" \"Bypass_DataDome\"\n \" fastlyFF=\" fastly.ff.visits_this_service;\n ##\n {{/if}}\n}\n\n# we\u0027re using the first restart for datadome, update a part of fastly code\n# we can\u0027t replace whole macros because we haven\u0027t got any idea about backends\nif (req.restarts == 1) {\n if (!req.http.x-timer) {\n set req.http.x-timer = \"S\" time.start.sec \".\" time.start.usec_frac;\n }\n set req.http.x-timer = req.http.x-timer \",VS0\";\n}\n\nset var.fastly_req_do_shield = (req.restarts \u003c= 1);", "type": "recv" }, { @@ -88,5 +102,5 @@ "type": "pass" } ], - "version": "2.19.4" + "version": "2.23.0" }