diff --git a/documentation/docs/backend/Backend/Backend.mdx b/documentation/docs/backend/Backend/Backend.mdx
index 79a4f6d59a..3db45735ce 100644
--- a/documentation/docs/backend/Backend/Backend.mdx
+++ b/documentation/docs/backend/Backend/Backend.mdx
@@ -10,18 +10,9 @@ import {Fiddle} from '@site/src/components/fiddle';
The **`Backend` constructor** lets you dynamically create new [Fastly Backends](https://developer.fastly.com/reference/api/services/backend/) for your Fastly Compute service.
-Dynamically creating new [Fastly Backends](https://developer.fastly.com/reference/api/services/backend/) is disabled by default for Fastly Services. Please contact [Fastly Support](https://support.fastly.com/hc/requests/new?ticket_form_id=360000269711) to request the feature be enabled on the Fastly Services which require Dynamic Backends.
+>**Note**: Dynamic backends are by default disabled at the Fastly service level. Contact [Fastly Support](https://support.fastly.com/hc/en-us/requests/new?ticket_form_id=360000269711) to request dynamic backends on Fastly Services.
-By default, Dynamic Backends are disabled within a JavaScript application as it can be a potential avenue for third-party JavaScript code to send requests, potentially including sensitive/secret data, off to destinations that the JavaScript project was not intending, which could be a security issue.
-
-To enable Dynamic Backends the application will need to explicitly allow Dynamic Backends via:
-
-```js
-import { allowDynamicBackends } from "fastly:experimental";
-allowDynamicBackends(true);
-```
-
-**Note**: Backend constructors can only be used when processing requests, not during build-time initialization.
+To disable the usage of dynamic backends, see [enforceExplicitBackends](../enforceExplicitBackends.mdx).
## Syntax
@@ -122,58 +113,8 @@ A new `Backend` object.
## Examples
-In this example an implicit Dynamic Backend is created when making the fetch request to and the response is then returned to the client.
-
-import { allowDynamicBackends } from "fastly:experimental";
-allowDynamicBackends(true);
-async function app() {
- // For any request, return the fastly homepage -- without defining a backend!
- return fetch('https://www.fastly.com/');
-}
-addEventListener("fetch", event => event.respondWith(app(event)));
-`
- },
- "requests": [
- {
- "enableCluster": true,
- "enableShield": false,
- "enableWAF": false,
- "method": "GET",
- "path": "/status=200",
- "useFreshCache": false,
- "followRedirects": false,
- "tests": "",
- "delay": 0
- }
- ],
- "srcVersion": 1
-}}>
-
-```js
-///
-import { allowDynamicBackends } from "fastly:experimental";
-allowDynamicBackends(true);
-async function app() {
- // For any request, return the fastly homepage -- without defining a backend!
- return fetch('https://www.fastly.com/');
-}
-addEventListener("fetch", event => event.respondWith(app(event)));
-```
-
-
-
In this example an explicit Dynamic Backend is created and supplied to the fetch request, the response is then returned to the client.
-
-import { allowDynamicBackends } from "fastly:experimental";
import { Backend } from "fastly:backend";
-allowDynamicBackends(true);
async function app() {
// For any request, return the fastly homepage -- without defining a backend!
const backend = new Backend({
@@ -225,9 +164,7 @@ addEventListener("fetch", event => event.respondWith(app(event)));
```js
///
-import { allowDynamicBackends } from "fastly:experimental";
import { Backend } from "fastly:backend";
-allowDynamicBackends(true);
async function app() {
// For any request, return the fastly homepage -- without defining a backend!
const backend = new Backend({
diff --git a/documentation/docs/backend/Backend/prototype/name.mdx b/documentation/docs/backend/Backend/prototype/name.mdx
new file mode 100644
index 0000000000..bb4081114b
--- /dev/null
+++ b/documentation/docs/backend/Backend/prototype/name.mdx
@@ -0,0 +1,76 @@
+---
+hide_title: false
+hide_table_of_contents: false
+pagination_next: null
+pagination_prev: null
+---
+import {Fiddle} from '@site/src/components/fiddle';
+
+# name
+
+The read-only **`name`** property of the backend returns the backend name string.
+
+## Value
+
+A `string`.
+
+## Description
+
+Provides the name of the backend.
+
+## Examples
+
+### Using name
+
+The following example logs the string value of a [Backend](../Backend.mdx) object:
+
+
+import { Backend } from "fastly:backend";
+async function app() {
+ const backend = new Backend({
+ name: "fastly",
+ target: "fastly.com",
+ });
+ console.log(backend.name); // "fastly"
+}
+addEventListener("fetch", event => event.respondWith(app(event)));
+`
+ },
+ "requests": [
+ {
+ "enableCluster": true,
+ "enableShield": false,
+ "enableWAF": false,
+ "method": "GET",
+ "path": "/status=200",
+ "useFreshCache": false,
+ "followRedirects": false,
+ "tests": "",
+ "delay": 0
+ }
+ ],
+ "srcVersion": 1
+}}>
+
+```js
+import { Backend } from "fastly:backend";
+async function app() {
+ const backend = new Backend({
+ name: "fastly",
+ target: "fastly.com",
+ });
+ console.log(backend.name); // "fastly"
+}
+addEventListener("fetch", event => event.respondWith(app(event)));
+```
+
+
\ No newline at end of file
diff --git a/documentation/docs/backend/Backend/prototype/toName.mdx b/documentation/docs/backend/Backend/prototype/toName.mdx
index f7667882de..751cbef129 100644
--- a/documentation/docs/backend/Backend/prototype/toName.mdx
+++ b/documentation/docs/backend/Backend/prototype/toName.mdx
@@ -7,6 +7,12 @@ pagination_prev: null
# Backend.prototype.toName()
+:::info
+
+This method is deprecated, use [`Backend.prototype.name`](./name.mdx) instead.
+
+:::
+
The **`toName()`** method returns the name associated with the `Backend` instance.
## Syntax
diff --git a/documentation/docs/backend/Backend/prototype/toString.mdx b/documentation/docs/backend/Backend/prototype/toString.mdx
index 8dd7a21ec9..5e8bb1a987 100644
--- a/documentation/docs/backend/Backend/prototype/toString.mdx
+++ b/documentation/docs/backend/Backend/prototype/toString.mdx
@@ -8,6 +8,12 @@ import {Fiddle} from '@site/src/components/fiddle';
# toString
+:::info
+
+This method is deprecated, use [`Backend.prototype.name`](./name.mdx) instead.
+
+:::
+
The **`toString()`** method returns a string representing the specified Backend value.
## Syntax
diff --git a/documentation/docs/backend/allowDynamicBackends.mdx b/documentation/docs/backend/allowDynamicBackends.mdx
new file mode 100644
index 0000000000..ede53cc65f
--- /dev/null
+++ b/documentation/docs/backend/allowDynamicBackends.mdx
@@ -0,0 +1,38 @@
+---
+hide_title: false
+hide_table_of_contents: false
+pagination_next: null
+pagination_prev: null
+---
+import {Fiddle} from '@site/src/components/fiddle';
+
+# allowDynamicBackends
+
+:::info
+
+This method is deprecated, and dynamic backends are now always supported when enabled at the service level. See [`enforceExplicitBackends`](./enforceExplicitBackends.mdx) instead.
+
+:::
+
+The **`allowDynamicBackends()`** function is used to control whether or not Dynamic Backends should be allowed within this Fastly Compute Service.
+
+By default, Dynamic Backends are enabled, but can be a potential security concern since third-party JavaScript code may send arbitrary requests, potentially including sensitive/secret data, off to destinations that the JavaScript project was not intending.
+
+Using `allowDynamicBackends(false)` this security property can be restored to only use explicit backend definitions.
+
+>**Note**: By default, while dynamic backends are allowed in the SDK, they are by default disabled at the Fastly service level.
+
+## Syntax
+
+```js
+allowDynamicBackends(enabledOrConfig)
+```
+
+### Parameters
+
+- `enabled` _: boolean_
+ - Whether or not to allow Dynamic Backends
+
+### Return value
+
+`undefined`.
diff --git a/documentation/docs/backend/enforceExplicitBackends.mdx b/documentation/docs/backend/enforceExplicitBackends.mdx
new file mode 100644
index 0000000000..9c4bf95ced
--- /dev/null
+++ b/documentation/docs/backend/enforceExplicitBackends.mdx
@@ -0,0 +1,43 @@
+---
+hide_title: false
+hide_table_of_contents: false
+pagination_next: null
+pagination_prev: null
+---
+
+# enforceExplicitBackends
+
+Call this function to enforce the security property of explicitly-defined backends, even when dynamic backends are enabled at
+the Fastly service level.
+
+By default, if dynamic backends are supported for the Fastly service, they will be automatically used when creating a new
+`fetch()` request. This default behaviour for dynamic backends can be a potential security concern since third-party JavaScript
+code may send arbitrary requests, including sensitive/secret data, off to destinations that the JavaScript project was not
+intending.
+
+When calling this function, an optional default backend name can be provided.
+
+>**Note**: This is a separate option to the service-level dynamic backend support for Fastly services, which is by deault disabled for Fastly services.
+
+The **`enforceExplicitBackends()`** function is used to control whether or not Dynamic Backends should be allowed within this Fastly Compute Service.
+
+By default, Dynamic Backends are enabled, but can be a potential security concern since third-party JavaScript code may send arbitrary requests, potentially including sensitive/secret data, off to destinations that the JavaScript project was not intending.
+
+Using `allowDynamicBackends(false)` this security property can be restored to only use explicit backend definitions.
+
+>**Note**: Dynamic Backends are disabled by default for Fastly Services. Please contact [Fastly Support](https://support.fastly.com/hc/requests/new?ticket_form_id=360000269711) to request the feature be enabled or disabled on Fastly Services.
+
+## Syntax
+
+```js
+enforceExplicitBackends(defaultBackend?)
+```
+
+### Parameters
+
+- `defaultBackend` _: string_ _**optional**_
+ - An optional default backend string name to use in `fetch()` requests.
+
+### Return value
+
+`undefined`.
diff --git a/documentation/docs/globals/fetch.mdx b/documentation/docs/globals/fetch.mdx
index 32581670e4..a8f05b89b3 100644
--- a/documentation/docs/globals/fetch.mdx
+++ b/documentation/docs/globals/fetch.mdx
@@ -23,6 +23,26 @@ not_ reject on HTTP errors (`404`, etc.). Instead, a
> **Note:** The `fetch()` method's parameters are identical to
> those of the `Request()` constructor.
+## Explicit Backends
+
+Internally, Fastly uses named backends to handle fetch requests, which need to be explicitly defined to enable custom HTTP origins to be fetched by the service.
+
+This `backend` option is then a special Fastly-specific fetch option that is provided to the `fetch()` call:
+
+```js
+fetch('https://origin.com/path', { backend: 'origin' });
+```
+
+Backends are configured using the Fastly service backend configuration, see the [Backend documentation](https://developer.fastly.com/reference/api/services/backend/) for more information.
+
+## Dynamic Backends
+
+Dynamic backends are a compute feature that allow services to define backends for themselves. This is a service-level Fastly feature that must be enabled through [Fastly Support](https://support.fastly.com/hc/en-us/requests/new?ticket_form_id=360000269711).
+
+When dynamic backends are enabled at the service level, the explicit `backend` option is no longer required for `fetch()` requests, and will instead be automatically created.
+
+In addition, custom backend confiuration options can then also be provided through the [`Backend()`](../fastly:backend/Backend/Backend.mdx) constructor.
+
## Syntax
```js
diff --git a/integration-tests/js-compute/fixtures/app/src/dynamic-backend.js b/integration-tests/js-compute/fixtures/app/src/dynamic-backend.js
index f47505f45e..a3c3d12a81 100644
--- a/integration-tests/js-compute/fixtures/app/src/dynamic-backend.js
+++ b/integration-tests/js-compute/fixtures/app/src/dynamic-backend.js
@@ -1,7 +1,11 @@
///
-import { Backend, setDefaultDynamicBackendConfig } from 'fastly:backend';
-import { CacheOverride } from 'fastly:cache-override';
+import {
+ Backend,
+ setDefaultDynamicBackendConfig,
+ enforceExplicitBackends,
+} from 'fastly:backend';
import { allowDynamicBackends } from 'fastly:experimental';
+import { CacheOverride } from 'fastly:cache-override';
import {
assert,
assertDoesNotThrow,
@@ -55,6 +59,10 @@ routes.set('/backend/timeout', async () => {
allowDynamicBackends(true);
await assertResolves(() => fetch('https://http-me.glitch.me/headers'));
await assertResolves(() => fetch('https://www.fastly.com'));
+ enforceExplicitBackends();
+ await assertRejects(() => fetch('https://www.fastly.com'));
+ enforceExplicitBackends('TheOrigin');
+ await assertResolves(() => fetch('https://www.fastly.com'));
});
routes.set(
'/implicit-dynamic-backend/dynamic-backends-enabled-called-twice',
@@ -225,6 +233,7 @@ routes.set('/backend/timeout', async () => {
actual = Reflect.ownKeys(Backend.prototype);
expected = [
'constructor',
+ 'name',
'isDynamic',
'target',
'hostOverride',
@@ -2432,6 +2441,9 @@ routes.set('/backend/timeout', async () => {
{
const backend = createValidFastlyBackend() ?? validFastlyBackend;
strictEqual(backend.isDynamic, true, 'isDymamic');
+ strictEqual(backend.name, 'fastly');
+ strictEqual(backend.toString(), 'fastly');
+ strictEqual(backend.toName(), 'fastly');
strictEqual(backend.target, 'www.fastly.com', 'target');
strictEqual(backend.hostOverride, 'www.fastly.com', 'override');
strictEqual(backend.port, 443, 'port');
@@ -2466,6 +2478,9 @@ routes.set('/backend/timeout', async () => {
{
const backend = createValidHttpMeBackend() ?? validHttpMeBackend;
strictEqual(backend.isDynamic, true, 'isDynamic');
+ strictEqual(backend.name, 'http-me');
+ strictEqual(backend.toString(), 'http-me');
+ strictEqual(backend.toName(), 'http-me');
strictEqual(backend.target, 'http-me.glitch.me', 'target');
strictEqual(backend.hostOverride, 'http-me.glitch.me', 'hostOverride');
strictEqual(backend.port, 443, 'port');
diff --git a/runtime/fastly/builtins/backend.cpp b/runtime/fastly/builtins/backend.cpp
index 1bdea1f50a..eca9e35ba8 100644
--- a/runtime/fastly/builtins/backend.cpp
+++ b/runtime/fastly/builtins/backend.cpp
@@ -1259,12 +1259,12 @@ JSString *Backend::name(JSContext *cx, JSObject *self) {
return JS_NewStringCopyZ(cx, backend->name().begin());
}
-bool Backend::to_string(JSContext *cx, unsigned argc, JS::Value *vp) {
+bool Backend::name_get(JSContext *cx, unsigned argc, JS::Value *vp) {
METHOD_HEADER(0);
auto backend = get_backend(cx, self);
if (!backend) {
- args.rval().setMagic(JSWhyMagic::JS_UNINITIALIZED_LEXICAL);
+ args.rval().setUndefined();
return true;
}
auto &name = backend->name();
@@ -1658,10 +1658,11 @@ const JSFunctionSpec Backend::static_methods[] = {
JS_FN("exists", exists, 1, JSPROP_ENUMERATE), JS_FN("fromName", from_name, 1, JSPROP_ENUMERATE),
JS_FN("health", health_for_name, 1, JSPROP_ENUMERATE), JS_FS_END};
const JSPropertySpec Backend::static_properties[] = {JS_PS_END};
-const JSFunctionSpec Backend::methods[] = {JS_FN("toString", to_string, 0, JSPROP_ENUMERATE),
- JS_FN("toName", to_string, 0, JSPROP_ENUMERATE),
+const JSFunctionSpec Backend::methods[] = {JS_FN("toString", name_get, 0, JSPROP_ENUMERATE),
+ JS_FN("toName", name_get, 0, JSPROP_ENUMERATE),
JS_FS_END};
const JSPropertySpec Backend::properties[] = {
+ JS_PSG("name", name_get, JSPROP_ENUMERATE),
JS_PSG("isDynamic", is_dynamic_get, JSPROP_ENUMERATE),
JS_PSG("target", target_get, JSPROP_ENUMERATE),
JS_PSG("hostOverride", host_override_get, JSPROP_ENUMERATE),
@@ -1751,6 +1752,12 @@ JSObject *Backend::create(JSContext *cx, JS::HandleObject request) {
auto res = host_api::HttpReq::register_dynamic_backend(host_backend->name(), target_string,
backend_config);
if (auto *err = res.to_err()) {
+ if (host_api::error_is_unsupported(*err)) {
+ JS_ReportErrorNumberASCII(cx, FastlyGetErrorMessage, nullptr,
+ JSMSG_DYNAMIC_BACKENDS_UNSUPPORTED_IMPLICIT, target_string.data(),
+ url_string.data);
+ return nullptr;
+ }
HANDLE_ERROR(cx, *err);
return nullptr;
}
@@ -1814,6 +1821,12 @@ bool Backend::constructor(JSContext *cx, unsigned argc, JS::Value *vp) {
auto res = host_api::HttpReq::register_dynamic_backend(host_backend->name(), target_string,
backend_config);
if (auto *err = res.to_err()) {
+ if (host_api::error_is_unsupported(*err)) {
+ JS_ReportErrorNumberASCII(cx, FastlyGetErrorMessage, nullptr,
+ JSMSG_DYNAMIC_BACKENDS_UNSUPPORTED_EXPLICIT,
+ target_string_slice.data);
+ return false;
+ }
HANDLE_ERROR(cx, *err);
return false;
}
@@ -1846,6 +1859,29 @@ bool set_default_backend_config(JSContext *cx, unsigned argc, JS::Value *vp) {
return true;
}
+// TODO: in next major, when global and fastly experimental are deprecated,
+// make it so that calling twice always throws an already enforced error.
+// and possibly also don't allow changing the default again.
+bool enforce_explicit_backends(JSContext *cx, unsigned argc, JS::Value *vp) {
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ auto default_backend_val = args.get(0);
+ if (!default_backend_val.isNullOrUndefined()) {
+ if (!default_backend_val.isString()) {
+ api::throw_error(cx, api::Errors::TypeError, "enforceExplicitBackends", "defaultBackend",
+ "be undefined or a string");
+ return false;
+ }
+ JS::RootedString backend(cx, JS::ToString(cx, default_backend_val));
+ if (!backend) {
+ return false;
+ }
+ Fastly::defaultBackend = backend;
+ }
+ Fastly::allowDynamicBackends = false;
+ args.rval().setUndefined();
+ return true;
+}
+
bool install(api::Engine *engine) {
JS::RootedObject backends(engine->cx(), JS_NewPlainObject(engine->cx()));
if (!backends) {
@@ -1873,6 +1909,18 @@ bool install(api::Engine *engine) {
set_default_backend_config_val)) {
return false;
}
+
+ auto enforce_explicit_backends_fn =
+ JS_NewFunction(engine->cx(), &enforce_explicit_backends, 1, 0, "enforceExplicitBackends");
+ RootedObject enforce_explicit_backends_obj(engine->cx(),
+ JS_GetFunctionObject(enforce_explicit_backends_fn));
+ RootedValue enforce_explicit_backends_val(engine->cx(),
+ JS::ObjectValue(*enforce_explicit_backends_obj));
+ if (!JS_SetProperty(engine->cx(), backend_ns, "enforceExplicitBackends",
+ enforce_explicit_backends_val)) {
+ return false;
+ }
+
RootedValue backend_ns_val(engine->cx(), JS::ObjectValue(*backend_ns));
if (!engine->define_builtin_module("fastly:backend", backend_ns_val)) {
return false;
diff --git a/runtime/fastly/builtins/backend.h b/runtime/fastly/builtins/backend.h
index d2c7f1fc10..9dead35d43 100644
--- a/runtime/fastly/builtins/backend.h
+++ b/runtime/fastly/builtins/backend.h
@@ -31,10 +31,10 @@ class Backend : public builtins::FinalizableBuiltinImpl {
static bool health_for_name(JSContext *cx, unsigned argc, JS::Value *vp);
// prototype methods
- static bool to_name(JSContext *cx, unsigned argc, JS::Value *vp);
- static bool to_string(JSContext *cx, unsigned argc, JS::Value *vp);
static bool health(JSContext *cx, unsigned argc, JS::Value *vp);
+ // getters
+ static bool name_get(JSContext *cx, unsigned argc, JS::Value *vp);
static bool is_dynamic_get(JSContext *cx, unsigned argc, JS::Value *vp);
static bool target_get(JSContext *cx, unsigned argc, JS::Value *vp);
static bool host_override_get(JSContext *cx, unsigned argc, JS::Value *vp);
diff --git a/runtime/fastly/builtins/fastly.cpp b/runtime/fastly/builtins/fastly.cpp
index 80effbb2e0..73222fce20 100644
--- a/runtime/fastly/builtins/fastly.cpp
+++ b/runtime/fastly/builtins/fastly.cpp
@@ -49,7 +49,8 @@ const JSErrorFormatString *FastlyGetErrorMessage(void *userRef, unsigned errorNu
JS::PersistentRooted Fastly::env;
JS::PersistentRooted Fastly::baseURL;
JS::PersistentRooted Fastly::defaultBackend;
-bool Fastly::allowDynamicBackends = false;
+bool allowDynamicBackendsCalled = false;
+bool Fastly::allowDynamicBackends = true;
bool Fastly::dump(JSContext *cx, unsigned argc, JS::Value *vp) {
JS::CallArgs args = CallArgsFromVp(argc, vp);
@@ -373,6 +374,9 @@ bool Fastly::defaultBackend_set(JSContext *cx, unsigned argc, JS::Value *vp) {
return false;
defaultBackend = backend;
+ if (!allowDynamicBackendsCalled) {
+ allowDynamicBackends = false;
+ }
args.rval().setUndefined();
return true;
}
@@ -395,6 +399,7 @@ bool Fastly::allowDynamicBackends_set(JSContext *cx, unsigned argc, JS::Value *v
} else {
allowDynamicBackends = JS::ToBoolean(set_value);
}
+ allowDynamicBackendsCalled = true;
args.rval().setUndefined();
return true;
}
diff --git a/runtime/fastly/builtins/fetch/request-response.cpp b/runtime/fastly/builtins/fetch/request-response.cpp
index c04e183c49..86e437536f 100644
--- a/runtime/fastly/builtins/fetch/request-response.cpp
+++ b/runtime/fastly/builtins/fetch/request-response.cpp
@@ -1796,7 +1796,7 @@ JSObject *Request::create(JSContext *cx, JS::HandleObject requestInstance, JS::H
if (!url_instance)
return nullptr;
- JS::RootedObject parsedURL(cx, URL::create(cx, url_instance, input, WorkerLocation::url));
+ JS::RootedObject parsedURL(cx, URL::create(cx, url_instance, input, fastly::Fastly::baseURL));
// 2. If `parsedURL` is failure, then throw a `TypeError`.
if (!parsedURL) {
diff --git a/runtime/fastly/host-api/error_numbers.msg b/runtime/fastly/host-api/error_numbers.msg
index 534091d223..34c0684ac6 100644
--- a/runtime/fastly/host-api/error_numbers.msg
+++ b/runtime/fastly/host-api/error_numbers.msg
@@ -79,6 +79,8 @@ MSG_DEF(JSMSG_SECRET_STORE_NAME_TOO_LONG, 0, JSEXN_TYPEERR,
MSG_DEF(JSMSG_SECRET_STORE_FROM_BYTES_INVALID_BUFFER, 0, JSEXN_TYPEERR, "SecretStore.fromBytes: bytes must be an ArrayBuffer or ArrayBufferView object")
MSG_DEF(JSMSG_READABLE_STREAM_LOCKED_OR_DISTRUBED, 0, JSEXN_TYPEERR, "Can't use a ReadableStream that's locked or has ever been read from or canceled")
MSG_DEF(JSMSG_INVALID_CHARACTER_ERROR, 0, JSEXN_ERR, "String contains an invalid character")
+MSG_DEF(JSMSG_DYNAMIC_BACKENDS_UNSUPPORTED_EXPLICIT, 2, JSEXN_ERR, "fetch(): No backend provided fetching '{1}'. Since dynamic backends are not enabled for this Fastly service, `fetch()` requires an explicit backend parameter. See https://js-compute-reference-docs.edgecompute.app/docs/globals/fetch for more info. Alternatively, contact Fastly support to enable dynamic backends.")
+MSG_DEF(JSMSG_DYNAMIC_BACKENDS_UNSUPPORTED_IMPLICIT, 1, JSEXN_ERR, "Backend constructor: Unable to create a dynamic backend for '{0}' - dynamic backends are unsupported on this service. Either explicitly configure backend services or contact Fastly support to enable dynamic backends.")
MSG_DEF(JSMSG_BACKEND_FROMNAME_BACKEND_DOES_NOT_EXIST, 1, JSEXN_ERR, "Backend.fromName: backend named '{0}' does not exist")
MSG_DEF(JSMSG_BACKEND_IS_HEALTHY_BACKEND_DOES_NOT_EXIST, 1, JSEXN_ERR, "Backend.health: backend named '{0}' does not exist")
MSG_DEF(JSMSG_BACKEND_PARAMETER_NOT_OBJECT, 0, JSEXN_TYPEERR, "Backend constructor: configuration parameter must be an Object")
diff --git a/src/bundle.js b/src/bundle.js
index 342e2ae28e..1bd6213b1e 100644
--- a/src/bundle.js
+++ b/src/bundle.js
@@ -16,6 +16,12 @@ let fastlyPlugin = {
contents: `
export const Backend = globalThis.Backend;
export const setDefaultDynamicBackendConfig = Object.getOwnPropertyDescriptor(globalThis.fastly, 'allowDynamicBackends').set;
+const allowDynamicBackends = Object.getOwnPropertyDescriptor(globalThis.fastly, 'allowDynamicBackends').set;
+export const setDefaultBackend = Object.getOwnPropertyDescriptor(globalThis.fastly, 'defaultBackend').set;
+export function enforceExplicitBackends (defaultBackend) {
+ allowDynamicBackends(false);
+ if (defaultBackend) setDefaultBackend(defaultBackend);
+}
`,
};
}
diff --git a/tests/wpt-harness/post-harness.js b/tests/wpt-harness/post-harness.js
index 3599701e6f..0f105b2ca8 100644
--- a/tests/wpt-harness/post-harness.js
+++ b/tests/wpt-harness/post-harness.js
@@ -1,9 +1,8 @@
/* eslint-env serviceworker */
/* global add_completion_callback setup done */
-import { enableDebugLogging, setDefaultBackend, setBaseURL } from "fastly:experimental";
+import { enableDebugLogging, setBaseURL } from "fastly:experimental";
enableDebugLogging(true);
-setDefaultBackend("wpt");
let completionPromise = new Promise((resolve) => {
add_completion_callback(function(tests, harness_status, asserts) {
diff --git a/types/backend.d.ts b/types/backend.d.ts
index 25a8d1d87b..acb66963ce 100644
--- a/types/backend.d.ts
+++ b/types/backend.d.ts
@@ -1,10 +1,47 @@
///
+///
declare module 'fastly:backend' {
+ /**
+ * Set the default backend configuration options for dynamic backends.
+ *
+ * Applies to backends created via {@link Backend | new Backend(...)} as well as for dynamic backends
+ * implicitly created when using {@link fetch | fetch()}.
+ *
+ * @note
+ * Dynamic backends are by default disabled at the Fastly service level.
+ * Contact [Fastly Support](https://support.fastly.com/hc/en-us/requests/new?ticket_form_id=360000269711)
+ * to request dynamic backends on Fastly Services.
+ *
+ * @param defaultDynamicBackendConfiguration default backend configuration options
+ */
export function setDefaultDynamicBackendConfig(
defaultDynamicBackendConfiguration: DefaultBackendConfiguration,
): void;
+ /**
+ * Call this function to enforce the security property of explicitly-defined backends, even
+ * when dynamic backends are enabled at the Fastly service level.
+ *
+ * By default, if dynamic backends are supported for the Fastly service, they will be automatically
+ * used when creating a new `fetch()` request. This default behaviour for dynamic backends can be a
+ * potential security concern since third-party JavaScript code may send arbitrary requests,
+ * including sensitive/secret data, off to destinations that the JavaScript project was not
+ * intending.
+ *
+ * When calling this function, an optional default backend name can be provided.
+ *
+ * @note
+ * This is a separate option to the service-level dynamic backend support for Fastly services.
+ * By default, dynamic backends are disabled for Fastly Services, so that even if not using this
+ * function, the service-level security configuration will apply.
+ *
+ * @param defaultBackend the name of the default backend to use, when using {@link fetch | fetch()}.
+ *
+ * @experimental
+ */
+ export function enforceExplicitBackends(defaultBackend?: string): void;
+
interface DefaultBackendConfiguration {
/**
* Maximum duration in milliseconds to wait for a connection to this backend to be established.
@@ -87,23 +124,25 @@ declare module 'fastly:backend' {
* Enables and sets the TCP keep alive options for the backend.
* Setting to boolean true enables keepalive with the default options.
*/
- tcpKeepalive?: boolean | {
- /**
- * Configure how long to wait after the last sent data over the TCP connection before
- * starting to send TCP keepalive probes.
- */
- timeSecs?: number;
+ tcpKeepalive?:
+ | boolean
+ | {
+ /**
+ * Configure how long to wait after the last sent data over the TCP connection before
+ * starting to send TCP keepalive probes.
+ */
+ timeSecs?: number;
- /**
- * Configure how long to wait between each TCP keepalive probe sent to the backend to determine if it is still active.
- */
- intervalSecs?: number;
+ /**
+ * Configure how long to wait between each TCP keepalive probe sent to the backend to determine if it is still active.
+ */
+ intervalSecs?: number;
- /**
- * Number of probes to send to the backend before it is considered dead.
- */
- probes?: number;
- };
+ /**
+ * Number of probes to send to the backend before it is considered dead.
+ */
+ probes?: number;
+ };
}
interface BackendConfiguration extends DefaultBackendConfiguration {
@@ -168,63 +207,11 @@ declare module 'fastly:backend' {
* Class for dynamically creating new [Fastly Backends](https://developer.fastly.com/reference/api/services/backend/).
*
* @note
- * This feature is in disabled by default for Fastly Services. Please contact [Fastly Support](https://support.fastly.com/hc/en-us/requests/new?ticket_form_id=360000269711) to request the feature be enabled on the Fastly Services which require Dynamic Backends.
- *
- * By default, Dynamic Backends are disabled within a JavaScript application as it can be a potential
- * avenue for third-party JavaScript code to send requests, potentially including sensitive/secret data,
- * off to destinations that the JavaScript project was not intending, which could be a security issue.
- *
- * To enable Dynamic Backends the application will need to explicitly allow Dynamic Backends via:
- * ```js
- * import { allowDynamicBackends } from "fastly:experimental";
- * allowDynamicBackends(true);
- * ```
+ * Dynamic backends are by default disabled at the Fastly service level.
+ * Contact [Fastly Support](https://support.fastly.com/hc/en-us/requests/new?ticket_form_id=360000269711)
+ * to request dynamic backends on Fastly Services.
*
- * **Note**: Can only be used when processing requests, not during build-time initialization.
- *
- * @example
- *
- * In this example an implicit Dynamic Backend is created when making the fetch request to https://www.fastly.com/ and the response is then returned to the client.
- *
- *
- *
+ * To disable the usage of dynamic backends, see {@link enforceExplicitBackends}.
*
* @example
* In this example an explicit Dynamic Backend is created and supplied to the fetch request, the response is then returned to the client.
@@ -238,7 +225,7 @@ declare module 'fastly:backend' {
* ],
* "src": {
* "deps": "{\n \"@fastly/js-compute\": \"^0.7.0\"\n}",
- * "main": "/// \nimport { allowDynamicBackends } from \"fastly:experimental\";\nimport { Backend } from \"fastly:backend\";\nallowDynamicBackends(true);\nasync function app() {\n // For any request, return the fastly homepage -- without defining a backend!\n const backend = new Backend({\n name: 'fastly',\n target: 'fastly.com',\n hostOverride: \"www.fastly.com\",\n connectTimeout: 1000,\n firstByteTimeout: 15000,\n betweenBytesTimeout: 10000,\n useSSL: true,\n sslMinVersion: 1.3,\n sslMaxVersion: 1.3,\n });\n return fetch('https://www.fastly.com/', {\n backend // Here we are configuring this request to use the backend from above.\n });\n}\naddEventListener(\"fetch\", event => event.respondWith(app(event)));\n"
+ * "main": "/// \nimport { Backend } from \"fastly:backend\";\nasync function app() {\n // For any request, return the fastly homepage -- without defining a backend!\n const backend = new Backend({\n name: 'fastly',\n target: 'fastly.com',\n hostOverride: \"www.fastly.com\",\n connectTimeout: 1000,\n firstByteTimeout: 15000,\n betweenBytesTimeout: 10000,\n useSSL: true,\n sslMinVersion: 1.3,\n sslMaxVersion: 1.3,\n });\n return fetch('https://www.fastly.com/', {\n backend // Here we are configuring this request to use the backend from above.\n });\n}\naddEventListener(\"fetch\", event => event.respondWith(app(event)));\n"
* },
* "requests": [
* {
@@ -259,9 +246,7 @@ declare module 'fastly:backend' {
*