Skip to content

Commit ddee2df

Browse files
kevinkassimory
authored andcommitted
Provide option to delete Deno namespace in worker (denoland#2717)
1 parent aaa7a3e commit ddee2df

18 files changed

+128
-17
lines changed

cli/main.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,10 @@ fn create_worker_and_state(
196196
s.status(status, msg).expect("shell problem");
197197
}
198198
});
199+
// TODO(kevinkassimo): maybe make include_deno_namespace also configurable?
199200
let state =
200-
ThreadSafeState::new(flags, argv, ops::op_selector_std, progress).unwrap();
201+
ThreadSafeState::new(flags, argv, ops::op_selector_std, progress, true)
202+
.unwrap();
201203
let worker = Worker::new(
202204
"main".to_string(),
203205
startup_data::deno_isolate_init(),

cli/msg.fbs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ table FormatErrorRes {
204204
// Create worker as host
205205
table CreateWorker {
206206
specifier: string;
207+
include_deno_namespace: bool;
207208
}
208209

209210
table CreateWorkerRes {

cli/ops.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2069,6 +2069,10 @@ fn op_create_worker(
20692069
let cmd_id = base.cmd_id();
20702070
let inner = base.inner_as_create_worker().unwrap();
20712071
let specifier = inner.specifier().unwrap();
2072+
// Only include deno namespace if requested AND current worker
2073+
// has included namespace (to avoid escalation).
2074+
let include_deno_namespace =
2075+
inner.include_deno_namespace() && state.include_deno_namespace;
20722076

20732077
let parent_state = state.clone();
20742078

@@ -2077,13 +2081,15 @@ fn op_create_worker(
20772081
parent_state.argv.clone(),
20782082
op_selector_std,
20792083
parent_state.progress.clone(),
2084+
include_deno_namespace,
20802085
)?;
20812086
let rid = child_state.resource.rid;
20822087
let name = format!("USER-WORKER-{}", specifier);
2088+
let deno_main_call = format!("denoMain({})", include_deno_namespace);
20832089

20842090
let mut worker =
20852091
Worker::new(name, startup_data::deno_isolate_init(), child_state);
2086-
worker.execute("denoMain()").unwrap();
2092+
worker.execute(&deno_main_call).unwrap();
20872093
worker.execute("workerMain()").unwrap();
20882094

20892095
let module_specifier = ModuleSpecifier::resolve_url_or_path(specifier)?;

cli/state.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ pub struct State {
8484
pub js_compiler: JsCompiler,
8585
pub json_compiler: JsonCompiler,
8686
pub ts_compiler: TsCompiler,
87+
88+
pub include_deno_namespace: bool,
8789
}
8890

8991
impl Clone for ThreadSafeState {
@@ -151,6 +153,7 @@ impl ThreadSafeState {
151153
argv_rest: Vec<String>,
152154
dispatch_selector: ops::OpSelector,
153155
progress: Progress,
156+
include_deno_namespace: bool,
154157
) -> Result<Self, ErrBox> {
155158
let custom_root = env::var("DENO_DIR").map(String::into).ok();
156159

@@ -223,6 +226,7 @@ impl ThreadSafeState {
223226
ts_compiler,
224227
js_compiler: JsCompiler {},
225228
json_compiler: JsonCompiler {},
229+
include_deno_namespace,
226230
};
227231

228232
Ok(ThreadSafeState(Arc::new(state)))
@@ -302,6 +306,7 @@ impl ThreadSafeState {
302306
argv,
303307
ops::op_selector_std,
304308
Progress::new(),
309+
true,
305310
)
306311
.unwrap()
307312
}

cli/worker.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ mod tests {
126126
argv,
127127
op_selector_std,
128128
Progress::new(),
129+
true,
129130
)
130131
.unwrap();
131132
let state_ = state.clone();
@@ -155,6 +156,7 @@ mod tests {
155156
argv,
156157
op_selector_std,
157158
Progress::new(),
159+
true,
158160
)
159161
.unwrap();
160162
let state_ = state.clone();
@@ -182,7 +184,7 @@ mod tests {
182184
let mut flags = flags::DenoFlags::default();
183185
flags.reload = true;
184186
let state =
185-
ThreadSafeState::new(flags, argv, op_selector_std, Progress::new())
187+
ThreadSafeState::new(flags, argv, op_selector_std, Progress::new(), true)
186188
.unwrap();
187189
let state_ = state.clone();
188190
tokio_util::run(lazy(move || {

core/shared_queue.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,12 @@ SharedQueue Binary Layout
2929
const INDEX_RECORDS = 3 + MAX_RECORDS;
3030
const HEAD_INIT = 4 * INDEX_RECORDS;
3131

32+
// Available on start due to bindings.
3233
const Deno = window[GLOBAL_NAMESPACE];
3334
const core = Deno[CORE_NAMESPACE];
35+
// Warning: DO NOT use window.Deno after this point.
36+
// It is possible that the Deno namespace has been deleted.
37+
// Use the above local Deno and core variable instead.
3438

3539
let sharedBytes;
3640
let shared32;
@@ -165,7 +169,7 @@ SharedQueue Binary Layout
165169
const success = push(control);
166170
// If successful, don't use first argument of core.send.
167171
const arg0 = success ? null : control;
168-
return window.Deno.core.send(arg0, zeroCopy);
172+
return Deno.core.send(arg0, zeroCopy);
169173
}
170174

171175
const denoCore = {

js/compiler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const console = new Console(core.print);
2525
window.console = console;
2626
window.workerMain = workerMain;
2727
export default function denoMain(): void {
28-
os.start("TS");
28+
os.start(true, "TS");
2929
}
3030

3131
const ASSETS = "$asset$";

js/core.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
22
import { window } from "./window";
33

4+
// This allows us to access core in API even if we
5+
// dispose window.Deno
46
export const core = window.Deno.core as DenoCore;

js/globals.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ import * as request from "./request";
3232
// These imports are not exposed and therefore are fine to just import the
3333
// symbols required.
3434
import { core } from "./core";
35-
import { immutableDefine } from "./util";
3635

3736
// During the build process, augmentations to the variable `window` in this
3837
// file are tracked and created as part of default library that is built into
@@ -71,7 +70,7 @@ window.window = window;
7170
// This is the Deno namespace, it is handled differently from other window
7271
// properties when building the runtime type library, as the whole module
7372
// is flattened into a single namespace.
74-
immutableDefine(window, "Deno", deno);
73+
window.Deno = deno;
7574
Object.freeze(window.Deno);
7675

7776
// Globally available functions and object instances.

js/main.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@ import { setLocation } from "./location";
1818
// builtin modules
1919
import * as deno from "./deno";
2020

21-
export default function denoMain(name?: string): void {
22-
const startResMsg = os.start(name);
21+
export default function denoMain(
22+
preserveDenoNamespace: boolean = true,
23+
name?: string
24+
): void {
25+
const startResMsg = os.start(preserveDenoNamespace, name);
2326

2427
setVersions(startResMsg.denoVersion()!, startResMsg.v8Version()!);
2528

js/os.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,10 @@ function sendStart(): msg.StartRes {
112112
// This function bootstraps an environment within Deno, it is shared both by
113113
// the runtime and the compiler environments.
114114
// @internal
115-
export function start(source?: string): msg.StartRes {
115+
export function start(
116+
preserveDenoNamespace = true,
117+
source?: string
118+
): msg.StartRes {
116119
core.setAsyncHandler(handleAsyncMsgFromRust);
117120

118121
// First we send an empty `Start` message to let the privileged side know we
@@ -124,9 +127,18 @@ export function start(source?: string): msg.StartRes {
124127

125128
setGlobals(startResMsg.pid(), startResMsg.noColor(), startResMsg.execPath()!);
126129

127-
// Deno.core could ONLY be safely frozen here (not in globals.ts)
128-
// since shared_queue.js will modify core properties.
129-
Object.freeze(window.Deno.core);
130+
if (preserveDenoNamespace) {
131+
util.immutableDefine(window, "Deno", window.Deno);
132+
// Deno.core could ONLY be safely frozen here (not in globals.ts)
133+
// since shared_queue.js will modify core properties.
134+
Object.freeze(window.Deno.core);
135+
// core.sharedQueue is an object so we should also freeze it.
136+
Object.freeze(window.Deno.core.sharedQueue);
137+
} else {
138+
// Remove window.Deno
139+
delete window.Deno;
140+
assert(window.Deno === undefined);
141+
}
130142

131143
return startResMsg;
132144
}

js/workers.ts

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,17 @@ export function decodeMessage(dataIntArray: Uint8Array): any {
2020
return JSON.parse(dataJson);
2121
}
2222

23-
function createWorker(specifier: string): number {
23+
function createWorker(
24+
specifier: string,
25+
includeDenoNamespace: boolean
26+
): number {
2427
const builder = flatbuffers.createBuilder();
2528
const specifier_ = builder.createString(specifier);
26-
const inner = msg.CreateWorker.createCreateWorker(builder, specifier_);
29+
const inner = msg.CreateWorker.createCreateWorker(
30+
builder,
31+
specifier_,
32+
includeDenoNamespace
33+
);
2734
const baseRes = sendSync(builder, msg.Any.CreateWorker, inner);
2835
assert(baseRes != null);
2936
assert(
@@ -149,6 +156,18 @@ export interface Worker {
149156
closed: Promise<void>;
150157
}
151158

159+
// TODO(kevinkassimo): Maybe implement reasonable web worker options?
160+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
161+
export interface WorkerOptions {}
162+
163+
/** Extended Deno Worker initialization options.
164+
* `noDenoNamespace` hides global `window.Deno` namespace for
165+
* spawned worker and nested workers spawned by it (default: false).
166+
*/
167+
export interface DenoWorkerOptions extends WorkerOptions {
168+
noDenoNamespace?: boolean;
169+
}
170+
152171
export class WorkerImpl implements Worker {
153172
private readonly rid: number;
154173
private isClosing: boolean = false;
@@ -157,8 +176,12 @@ export class WorkerImpl implements Worker {
157176
public onmessage?: (data: any) => void;
158177
public onmessageerror?: () => void;
159178

160-
constructor(specifier: string) {
161-
this.rid = createWorker(specifier);
179+
constructor(specifier: string, options?: DenoWorkerOptions) {
180+
let includeDenoNamespace = true;
181+
if (options && options.noDenoNamespace) {
182+
includeDenoNamespace = false;
183+
}
184+
this.rid = createWorker(specifier, includeDenoNamespace);
162185
this.run();
163186
this.isClosedPromise = hostGetWorkerClosed(this.rid);
164187
this.isClosedPromise.then(

tests/039_worker_deno_ns.test

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
args: run --reload tests/039_worker_deno_ns.ts
2+
output: tests/039_worker_deno_ns.ts.out

tests/039_worker_deno_ns.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
const w1 = new Worker("./tests/039_worker_deno_ns/has_ns.ts");
2+
const w2 = new Worker("./tests/039_worker_deno_ns/no_ns.ts", {
3+
noDenoNamespace: true
4+
});
5+
let w1MsgCount = 0;
6+
let w2MsgCount = 0;
7+
w1.onmessage = (msg): void => {
8+
console.log(msg.data);
9+
w1MsgCount++;
10+
if (w1MsgCount === 1) {
11+
w1.postMessage("CONTINUE");
12+
} else {
13+
w2.postMessage("START");
14+
}
15+
};
16+
w2.onmessage = (msg): void => {
17+
console.log(msg.data);
18+
w2MsgCount++;
19+
if (w2MsgCount === 1) {
20+
w2.postMessage("CONTINUE");
21+
} else {
22+
Deno.exit(0);
23+
}
24+
};
25+
w1.postMessage("START");

tests/039_worker_deno_ns.ts.out

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
has_ns.ts: is window.Deno available: true
2+
[SPAWNED BY has_ns.ts] maybe_ns.ts: is window.Deno available: true
3+
no_ns.ts: is window.Deno available: false
4+
[SPAWNED BY no_ns.ts] maybe_ns.ts: is window.Deno available: false

tests/039_worker_deno_ns/has_ns.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
onmessage = (msg): void => {
2+
if (msg.data === "START") {
3+
postMessage("has_ns.ts: is window.Deno available: " + !!window.Deno);
4+
} else {
5+
const worker = new Worker("./tests/039_worker_deno_ns/maybe_ns.ts");
6+
worker.onmessage = (msg): void => {
7+
postMessage("[SPAWNED BY has_ns.ts] " + msg.data);
8+
};
9+
}
10+
};

tests/039_worker_deno_ns/maybe_ns.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
postMessage("maybe_ns.ts: is window.Deno available: " + !!window.Deno);

tests/039_worker_deno_ns/no_ns.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
onmessage = (msg): void => {
2+
if (msg.data === "START") {
3+
postMessage("no_ns.ts: is window.Deno available: " + !!window.Deno);
4+
} else {
5+
const worker = new Worker("./tests/039_worker_deno_ns/maybe_ns.ts");
6+
worker.onmessage = (msg): void => {
7+
postMessage("[SPAWNED BY no_ns.ts] " + msg.data);
8+
};
9+
}
10+
};

0 commit comments

Comments
 (0)