Skip to content
File

Blob: src/workerd/api/unsafe.h

cpp203 lines
1#pragma once
2 
3#include <workerd/io/io-context.h>
4#include <workerd/jsg/jsg.h>
5#include <workerd/jsg/modules-new.h>
6#include <workerd/jsg/script.h>
7#include <workerd/jsg/url.h>
8 
9#include <csignal>
10#include <iostream>
11 
12#ifdef _WIN32
13#include <io.h>
14#else
15#include <unistd.h>
16#endif
17 
18#include <workerd/api/fuzzilli.h>
19 
20namespace workerd::api {
21 
22// A special binding object that allows for dynamic evaluation.
23class UnsafeEval: public jsg::Object {
24 public:
25 UnsafeEval() = default;
26 UnsafeEval(jsg::Lock&, const jsg::Url&) {}
27 
28 // A non-capturing eval. Compile and evaluates the given script, returning whatever
29 // value is returned by the script. This version of eval intentionally does not
30 // capture any part of the outer scope other than globalThis and globally scoped
31 // variables. The optional `name` will appear in stack traces for any errors thrown.
32 //
33 // console.log(env.unsafe.eval('1 + 1')); // prints 2
34 //
35 jsg::JsValue eval(jsg::Lock& js, kj::String script, jsg::Optional<kj::String> name);
36 
37 using UnsafeEvalFunction = jsg::Function<jsg::Value(jsg::Arguments<jsg::Value>)>;
38 
39 // Compiles and returns a new Function using the given script. The function does not
40 // capture any part of the outer scope other than globalThis and globally scoped
41 // variables. The optional `name` will be set as the name of the function and will
42 // appear in stack traces for any errors thrown. An optional list of argument names
43 // can be passed in.
44 //
45 // const fn = env.unsafe.newFunction('return m', 'foo', 'm');
46 // console.log(fn(1)); // prints 1
47 //
48 UnsafeEvalFunction newFunction(jsg::Lock& js,
49 jsg::JsString script,
50 jsg::Optional<kj::String> name,
51 jsg::Arguments<jsg::JsRef<jsg::JsString>> args,
52 const jsg::TypeHandler<UnsafeEvalFunction>& handler);
53 
54 // Compiles and returns a new Async Function using the given script. The function
55 // does not capture any part of the outer scope other than globalThis and globally
56 // scoped variables. The optional `name` will be set as the name of the function
57 // and will appear in stack traces for any errors thrown. An optional list of
58 // arguments names can be passed in. If your function needs to use the await
59 // key, use this instead of newFunction.
60 UnsafeEvalFunction newAsyncFunction(jsg::Lock& js,
61 jsg::JsString script,
62 jsg::Optional<kj::String> name,
63 jsg::Arguments<jsg::JsRef<jsg::JsString>> args,
64 const jsg::TypeHandler<UnsafeEvalFunction>& handler);
65 
66 jsg::JsValue newWasmModule(jsg::Lock& js, kj::Array<kj::byte> src);
67 
68 JSG_RESOURCE_TYPE(UnsafeEval) {
69 JSG_METHOD(eval);
70 JSG_METHOD(newFunction);
71 JSG_METHOD(newAsyncFunction);
72 JSG_METHOD(newWasmModule);
73 }
74};
75 
76// A special binding that allows access to stdin. Used for REPL.
77class Stdin: public jsg::Object {
78 public:
79 Stdin() = default;
80 
81 void reprl(jsg::Lock& js);
82 
83 kj::String getline(jsg::Lock& js) {
84 std::string res;
85 std::getline(std::cin, res);
86 return kj::heapString(res.c_str());
87 }
88 
89 JSG_RESOURCE_TYPE(Stdin) {
90 JSG_METHOD(getline);
91#ifdef WORKERD_FUZZILLI
92 JSG_METHOD(reprl);
93#endif
94 }
95};
96 
97class UnsafeModule: public jsg::Object {
98 public:
99 UnsafeModule() = default;
100 UnsafeModule(jsg::Lock&, const jsg::Url&) {}
101 jsg::Promise<void> abortAllDurableObjects(jsg::Lock& js);
102 
103 // Like abortAllDurableObjects(), but also deletes storage and cancels alarms so DOs
104 // restart with clean state. Namespaces with preventEviction are not affected.
105 jsg::Promise<void> deleteAllDurableObjects(jsg::Lock& js);
106 
107 // Returns true if the TEST_WORKERD autogate is enabled.
108 // This is used to verify that the all-autogates test variant is working correctly.
109 bool isTestAutogateEnabled();
110 
111 JSG_RESOURCE_TYPE(UnsafeModule) {
112 JSG_METHOD(abortAllDurableObjects);
113 JSG_METHOD(deleteAllDurableObjects);
114 JSG_METHOD(isTestAutogateEnabled);
115 }
116};
117 
118#ifdef WORKERD_FUZZILLI
119// Fuzzilli fuzzing support for triggering crashes and printing debug output
120class Fuzzilli: public jsg::Object {
121 public:
122 Fuzzilli() = default;
123 Fuzzilli(jsg::Lock&, const jsg::Url&) {}
124 
125 // Fuzzilli function for triggering crashes or printing debug output
126 // fuzzilli('FUZZILLI_CRASH', type: number): Triggers a crash based on type
127 // fuzzilli('FUZZILLI_PRINT', message: string): Prints message to fuzzer output
128 void fuzzilli(jsg::Lock& js, jsg::Arguments<jsg::Value> args);
129 
130 JSG_RESOURCE_TYPE(Fuzzilli) {
131 JSG_METHOD(fuzzilli);
132 }
133};
134#endif
135 
136template <class Registry>
137void registerUnsafeModule(Registry& registry) {
138 registry.template addBuiltinModule<UnsafeModule>(
139 "workerd:unsafe", workerd::jsg::ModuleRegistry::Type::BUILTIN);
140 registry.template addBuiltinModule<UnsafeEval>(
141 "workerd:unsafe-eval", workerd::jsg::ModuleRegistry::Type::BUILTIN);
142}
143 
144#ifdef WORKERD_FUZZILLI
145#define EW_UNSAFE_ISOLATE_TYPES api::UnsafeEval, api::UnsafeModule, api::Stdin, api::Fuzzilli
146#else
147#define EW_UNSAFE_ISOLATE_TYPES api::UnsafeEval, api::UnsafeModule, api::Stdin
148#endif
149 
150template <class Registry>
151void registerUnsafeModules(Registry& registry, auto featureFlags) {
152 registry.template addBuiltinModule<UnsafeEval>(
153 "internal:unsafe-eval", workerd::jsg::ModuleRegistry::Type::INTERNAL);
154#ifdef WORKERD_FUZZILLI
155 registry.template addBuiltinModule<Stdin>(
156 "workerd:stdin", workerd::jsg::ModuleRegistry::Type::BUILTIN);
157 
158 if (featureFlags.getWorkerdExperimental()) {
159 registry.template addBuiltinModule<Fuzzilli>(
160 "workerd:fuzzilli", workerd::jsg::ModuleRegistry::Type::BUILTIN);
161 }
162#endif
163}
164 
165template <typename TypeWrapper>
166kj::Own<jsg::modules::ModuleBundle> getInternalUnsafeModuleBundle(auto featureFlags) {
167 jsg::modules::ModuleBundle::BuiltinBuilder builder(
168 jsg::modules::ModuleBundle::BuiltinBuilder::Type::BUILTIN_ONLY);
169 static const auto kSpecifier = "internal:unsafe-eval"_url;
170 builder.addObject<UnsafeEval, TypeWrapper>(kSpecifier);
171 return builder.finish();
172}
173 
174template <typename TypeWrapper>
175kj::Own<jsg::modules::ModuleBundle> getExternalUnsafeModuleBundle(auto featureFlags) {
176 jsg::modules::ModuleBundle::BuiltinBuilder builder(
177 jsg::modules::ModuleBundle::BuiltinBuilder::Type::BUILTIN);
178 static const auto kSpecifier = "workerd:unsafe-eval"_url;
179 builder.addObject<UnsafeEval, TypeWrapper>(kSpecifier);
180 
181 static const auto kUnsafeSpecifier = "workerd:unsafe"_url;
182 builder.addObject<UnsafeModule, TypeWrapper>(kUnsafeSpecifier);
183 
184#ifdef WORKERD_FUZZILLI
185 {
186 static const auto kStdinSpecifier = "workerd:stdin"_url;
187 builder.addSynthetic(kStdinSpecifier,
188 jsg::modules::Module::newJsgObjectModuleHandler<Stdin, TypeWrapper>(
189 [](jsg::Lock& js) { return js.alloc<Stdin>(); }));
190 }
191 
192 if (featureFlags.getWorkerdExperimental()) {
193 static const auto kFuzzilliSpecifier = "workerd:fuzzilli"_url;
194 builder.addSynthetic(kFuzzilliSpecifier,
195 jsg::modules::Module::newJsgObjectModuleHandler<Fuzzilli, TypeWrapper>(
196 [](jsg::Lock& js) { return js.alloc<Fuzzilli>(); }));
197 }
198#endif
199 
200 return builder.finish();
201}
202} // namespace workerd::api