Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for unsafe eval bindings to miniflare #4322

Merged
merged 6 commits into from
Nov 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .changeset/tricky-otters-rescue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
"miniflare": minor
---

add `unsafeEvalBinding` option

Add option to leverage the newly introduced [`UnsafeEval`](https://github.com/cloudflare/workerd/pull/1338) workerd binding API,
such API is used to evaluate javascript code at runtime via the provided `eval` and `newFunction` methods.

The API, for security reasons (as per the [workers docs](https://developers.cloudflare.com/workers/runtime-apis/web-standards/#javascript-standards)), is not to be use in production but it is intended for local purposes only such as local testing.

To use the binding you need to specify a string value for the `unsafeEvalBinding`, such will be the name of the `UnsafeEval` bindings that will be made available in the workerd runtime.

For example the following code shows how to set the binding with the `UNSAFE_EVAL` name and evaluate the `1+1` string:

```ts
const mf = new Miniflare({
log,
modules: true,
script: `
export default {
fetch(req, env, ctx) {
const two = env.UNSAFE_EVAL.eval('1+1');
return new Response('two = ' + two); // returns 'two = 2'
}
}
`,
unsafeEvalBinding: "UNSAFE_EVAL",
});
```
9 changes: 9 additions & 0 deletions packages/miniflare/src/plugins/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ const CoreOptionsSchemaInput = z.intersection(
unsafeEphemeralDurableObjects: z.boolean().optional(),
unsafeDirectHost: z.string().optional(),
unsafeDirectPort: z.number().optional(),

unsafeEvalBinding: z.string().optional(),
})
);
export const CoreOptionsSchema = CoreOptionsSchemaInput.transform((value) => {
Expand Down Expand Up @@ -314,6 +316,13 @@ export const CORE_PLUGIN: Plugin<
);
}

if (options.unsafeEvalBinding !== undefined) {
bindings.push({
name: options.unsafeEvalBinding,
unsafeEval: kVoid,
});
}

return Promise.all(bindings);
},
async getNodeBindings(options) {
Expand Down
3 changes: 3 additions & 0 deletions packages/miniflare/src/runtime/config/workerd.capnp
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,9 @@ struct Worker {
# A binding for Hyperdrive. Allows workers to use Hyperdrive caching & pooling for Postgres
# databases.

unsafeEval @23 :Void;
# A simple binding that enables access to the UnsafeEval API.

# TODO(someday): dispatch, other new features
}

Expand Down
4 changes: 4 additions & 0 deletions packages/miniflare/src/runtime/config/workerd.capnp.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,7 @@ export declare enum Worker_Binding_Which {
FROM_ENVIRONMENT = 14,
ANALYTICS_ENGINE = 15,
HYPERDRIVE = 16,
UNSAFE_EVAL = 17,
}
export declare class Worker_Binding extends __S {
static readonly UNSPECIFIED = Worker_Binding_Which.UNSPECIFIED;
Expand All @@ -490,6 +491,7 @@ export declare class Worker_Binding extends __S {
static readonly FROM_ENVIRONMENT = Worker_Binding_Which.FROM_ENVIRONMENT;
static readonly ANALYTICS_ENGINE = Worker_Binding_Which.ANALYTICS_ENGINE;
static readonly HYPERDRIVE = Worker_Binding_Which.HYPERDRIVE;
static readonly UNSAFE_EVAL = Worker_Binding_Which.UNSAFE_EVAL;
static readonly Type: typeof Worker_Binding_Type;
static readonly DurableObjectNamespaceDesignator: typeof Worker_Binding_DurableObjectNamespaceDesignator;
static readonly CryptoKey: typeof Worker_Binding_CryptoKey;
Expand Down Expand Up @@ -601,6 +603,8 @@ export declare class Worker_Binding extends __S {
initHyperdrive(): Worker_Binding_Hyperdrive;
isHyperdrive(): boolean;
setHyperdrive(): void;
isUnsafeEval(): boolean;
setUnsafeEval(): void;
toString(): string;
which(): Worker_Binding_Which;
}
Expand Down
9 changes: 9 additions & 0 deletions packages/miniflare/src/runtime/config/workerd.capnp.js
Original file line number Diff line number Diff line change
Expand Up @@ -1291,6 +1291,8 @@ var Worker_Binding_Which;
"ANALYTICS_ENGINE";
Worker_Binding_Which[(Worker_Binding_Which["HYPERDRIVE"] = 16)] =
"HYPERDRIVE";
Worker_Binding_Which[(Worker_Binding_Which["UNSAFE_EVAL"] = 17)] =
"UNSAFE_EVAL";
})(
(Worker_Binding_Which =
exports.Worker_Binding_Which || (exports.Worker_Binding_Which = {}))
Expand Down Expand Up @@ -1736,6 +1738,12 @@ class Worker_Binding extends capnp_ts_1.Struct {
setHyperdrive() {
capnp_ts_1.Struct.setUint16(0, 16, this);
}
isUnsafeEval() {
return capnp_ts_1.Struct.getUint16(0, this) === 17;
}
setUnsafeEval() {
capnp_ts_1.Struct.setUint16(0, 17, this);
}
toString() {
return "Worker_Binding_" + super.toString();
}
Expand All @@ -1762,6 +1770,7 @@ Worker_Binding.QUEUE = Worker_Binding_Which.QUEUE;
Worker_Binding.FROM_ENVIRONMENT = Worker_Binding_Which.FROM_ENVIRONMENT;
Worker_Binding.ANALYTICS_ENGINE = Worker_Binding_Which.ANALYTICS_ENGINE;
Worker_Binding.HYPERDRIVE = Worker_Binding_Which.HYPERDRIVE;
Worker_Binding.UNSAFE_EVAL = Worker_Binding_Which.UNSAFE_EVAL;
Worker_Binding.Type = Worker_Binding_Type;
Worker_Binding.DurableObjectNamespaceDesignator =
Worker_Binding_DurableObjectNamespaceDesignator;
Expand Down
1 change: 1 addition & 0 deletions packages/miniflare/src/runtime/config/workerd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export type Worker_Binding = {
| { fromEnvironment?: string }
| { analyticsEngine?: ServiceDesignator }
| { hyperdrive?: Worker_Binding_Hyperdrive }
| { unsafeEval?: Void }
);

export interface Worker_Binding_Parameter {
Expand Down
30 changes: 29 additions & 1 deletion packages/miniflare/test/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,7 @@ test("Miniflare: `node:`, `cloudflare:` and `workerd:` modules", async (t) => {
script: `
import assert from "node:assert";
import { Buffer } from "node:buffer";
import { connect } from "cloudflare:sockets";
import { connect } from "cloudflare:sockets";
import rtti from "workerd:rtti";
export default {
fetch() {
Expand Down Expand Up @@ -1087,3 +1087,31 @@ unixSerialTest(
t.is(await res.text(), "When I grow up, I want to be a big workerd!");
}
);

test("Miniflare: allows the use of unsafe eval bindings", async (t) => {
const log = new TestLog(t);

const mf = new Miniflare({
log,
modules: true,
script: `
export default {
fetch(req, env, ctx) {
const three = env.UNSAFE_EVAL.eval('2 + 1');

const fn = env.UNSAFE_EVAL.newFunction(
'return \`the computed value is \${n}\`', '', 'n'
);

return new Response(fn(three));
}
}
`,
mrbbot marked this conversation as resolved.
Show resolved Hide resolved
unsafeEvalBinding: "UNSAFE_EVAL",
});
t.teardown(() => mf.dispose());

const response = await mf.dispatchFetch("http://localhost");
t.true(response.ok);
t.is(await response.text(), "the computed value is 3");
});
Loading