mirror of
https://github.com/denoland/deno.git
synced 2024-11-25 15:29:32 -05:00
fix(runtime/ops/worker_host): move permission arg parsing to Rust (#12297)
This commit is contained in:
parent
43a63530ac
commit
7a22df9b76
19 changed files with 1201 additions and 823 deletions
1
cli/dts/lib.deno.ns.d.ts
vendored
1
cli/dts/lib.deno.ns.d.ts
vendored
|
@ -2178,6 +2178,7 @@ declare namespace Deno {
|
||||||
|
|
||||||
export interface FfiPermissionDescriptor {
|
export interface FfiPermissionDescriptor {
|
||||||
name: "ffi";
|
name: "ffi";
|
||||||
|
path?: string | URL;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HrtimePermissionDescriptor {
|
export interface HrtimePermissionDescriptor {
|
||||||
|
|
6
cli/dts/lib.deno.unstable.d.ts
vendored
6
cli/dts/lib.deno.unstable.d.ts
vendored
|
@ -910,10 +910,12 @@ declare namespace Deno {
|
||||||
* If set to `"inherit"`, the current `ffi` permission will be inherited.
|
* If set to `"inherit"`, the current `ffi` permission will be inherited.
|
||||||
* If set to `true`, the global `ffi` permission will be requested.
|
* If set to `true`, the global `ffi` permission will be requested.
|
||||||
* If set to `false`, the global `ffi` permission will be revoked.
|
* If set to `false`, the global `ffi` permission will be revoked.
|
||||||
|
* If set to `Array<string | URL>`, the `ffi` permission will be requested with the
|
||||||
|
* specified file paths.
|
||||||
*
|
*
|
||||||
* Defaults to "inherit".
|
* Defaults to "inherit".
|
||||||
*/
|
*/
|
||||||
ffi?: "inherit" | boolean;
|
ffi?: "inherit" | boolean | Array<string | URL>;
|
||||||
|
|
||||||
/** Specifies if the `read` permission should be requested or revoked.
|
/** Specifies if the `read` permission should be requested or revoked.
|
||||||
* If set to `"inherit"`, the current `read` permission will be inherited.
|
* If set to `"inherit"`, the current `read` permission will be inherited.
|
||||||
|
@ -1237,7 +1239,7 @@ declare interface WorkerOptions {
|
||||||
* For example: `["https://deno.land", "localhost:8080"]`.
|
* For example: `["https://deno.land", "localhost:8080"]`.
|
||||||
*/
|
*/
|
||||||
net?: "inherit" | boolean | string[];
|
net?: "inherit" | boolean | string[];
|
||||||
ffi?: "inherit" | boolean;
|
ffi?: "inherit" | boolean | Array<string | URL>;
|
||||||
read?: "inherit" | boolean | Array<string | URL>;
|
read?: "inherit" | boolean | Array<string | URL>;
|
||||||
run?: "inherit" | boolean | Array<string | URL>;
|
run?: "inherit" | boolean | Array<string | URL>;
|
||||||
write?: "inherit" | boolean | Array<string | URL>;
|
write?: "inherit" | boolean | Array<string | URL>;
|
||||||
|
|
|
@ -198,7 +198,7 @@ pub struct Flags {
|
||||||
pub allow_env: Option<Vec<String>>,
|
pub allow_env: Option<Vec<String>>,
|
||||||
pub allow_hrtime: bool,
|
pub allow_hrtime: bool,
|
||||||
pub allow_net: Option<Vec<String>>,
|
pub allow_net: Option<Vec<String>>,
|
||||||
pub allow_ffi: Option<Vec<String>>,
|
pub allow_ffi: Option<Vec<PathBuf>>,
|
||||||
pub allow_read: Option<Vec<PathBuf>>,
|
pub allow_read: Option<Vec<PathBuf>>,
|
||||||
pub allow_run: Option<Vec<String>>,
|
pub allow_run: Option<Vec<String>>,
|
||||||
pub allow_write: Option<Vec<PathBuf>>,
|
pub allow_write: Option<Vec<PathBuf>>,
|
||||||
|
@ -324,7 +324,7 @@ impl Flags {
|
||||||
args.push("--allow-ffi".to_string());
|
args.push("--allow-ffi".to_string());
|
||||||
}
|
}
|
||||||
Some(ffi_allowlist) => {
|
Some(ffi_allowlist) => {
|
||||||
let s = format!("--allow-ffi={}", ffi_allowlist.join(","));
|
let s = format!("--allow-ffi={}", join_paths(ffi_allowlist, ","));
|
||||||
args.push(s);
|
args.push(s);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -2202,7 +2202,7 @@ fn permission_args_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ffi_wl) = matches.values_of("allow-ffi") {
|
if let Some(ffi_wl) = matches.values_of("allow-ffi") {
|
||||||
let ffi_allowlist: Vec<String> = ffi_wl.map(ToString::to_string).collect();
|
let ffi_allowlist: Vec<PathBuf> = ffi_wl.map(PathBuf::from).collect();
|
||||||
flags.allow_ffi = Some(ffi_allowlist);
|
flags.allow_ffi = Some(ffi_allowlist);
|
||||||
debug!("ffi allowlist: {:#?}", &flags.allow_ffi);
|
debug!("ffi allowlist: {:#?}", &flags.allow_ffi);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,8 @@ use deno_core::error::AnyError;
|
||||||
use deno_core::JsRuntime;
|
use deno_core::JsRuntime;
|
||||||
use deno_core::ModuleSpecifier;
|
use deno_core::ModuleSpecifier;
|
||||||
use deno_core::OpState;
|
use deno_core::OpState;
|
||||||
use deno_runtime::ops::worker_host::create_worker_permissions;
|
use deno_runtime::permissions::create_child_permissions;
|
||||||
use deno_runtime::ops::worker_host::PermissionsArg;
|
use deno_runtime::permissions::ChildPermissionsArg;
|
||||||
use deno_runtime::permissions::Permissions;
|
use deno_runtime::permissions::Permissions;
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
@ -26,15 +26,15 @@ struct PermissionsHolder(Uuid, Permissions);
|
||||||
|
|
||||||
pub fn op_pledge_test_permissions(
|
pub fn op_pledge_test_permissions(
|
||||||
state: &mut OpState,
|
state: &mut OpState,
|
||||||
args: PermissionsArg,
|
args: ChildPermissionsArg,
|
||||||
_: (),
|
_: (),
|
||||||
) -> Result<Uuid, AnyError> {
|
) -> Result<Uuid, AnyError> {
|
||||||
deno_runtime::ops::check_unstable(state, "Deno.test.permissions");
|
deno_runtime::ops::check_unstable(state, "Deno.test.permissions");
|
||||||
|
|
||||||
let token = Uuid::new_v4();
|
let token = Uuid::new_v4();
|
||||||
let parent_permissions = state.borrow::<Permissions>().clone();
|
let parent_permissions = state.borrow_mut::<Permissions>();
|
||||||
let worker_permissions =
|
let worker_permissions = create_child_permissions(parent_permissions, args)?;
|
||||||
create_worker_permissions(parent_permissions.clone(), args)?;
|
let parent_permissions = parent_permissions.clone();
|
||||||
|
|
||||||
state.put::<PermissionsHolder>(PermissionsHolder(token, parent_permissions));
|
state.put::<PermissionsHolder>(PermissionsHolder(token, parent_permissions));
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use crate::itest;
|
use crate::itest;
|
||||||
|
|
||||||
itest!(workers {
|
itest!(workers {
|
||||||
args: "test --reload --location http://127.0.0.1:4545/ --allow-net --allow-read --unstable workers/test.ts",
|
args: "test --reload --location http://127.0.0.1:4545/ -A --unstable workers/test.ts",
|
||||||
output: "workers/test.ts.out",
|
output: "workers/test.ts.out",
|
||||||
http_server: true,
|
http_server: true,
|
||||||
});
|
});
|
||||||
|
|
2
cli/tests/testdata/test/allow_all.ts
vendored
2
cli/tests/testdata/test/allow_all.ts
vendored
|
@ -18,7 +18,7 @@ for (const name of permissions) {
|
||||||
},
|
},
|
||||||
async fn() {
|
async fn() {
|
||||||
const status = await Deno.permissions.query({ name });
|
const status = await Deno.permissions.query({ name });
|
||||||
assertEquals(status.state, "denied");
|
assertEquals(status.state, "prompt");
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -6,12 +6,12 @@ self.onmessage = async () => {
|
||||||
const run = await Deno.permissions.query({ name: "run" });
|
const run = await Deno.permissions.query({ name: "run" });
|
||||||
const write = await Deno.permissions.query({ name: "write" });
|
const write = await Deno.permissions.query({ name: "write" });
|
||||||
self.postMessage(
|
self.postMessage(
|
||||||
hrtime.state === "denied" &&
|
hrtime.state === "prompt" &&
|
||||||
net.state === "denied" &&
|
net.state === "prompt" &&
|
||||||
ffi.state === "denied" &&
|
ffi.state === "prompt" &&
|
||||||
read.state === "denied" &&
|
read.state === "prompt" &&
|
||||||
run.state === "denied" &&
|
run.state === "prompt" &&
|
||||||
write.state === "denied",
|
write.state === "prompt",
|
||||||
);
|
);
|
||||||
self.close();
|
self.close();
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
const worker = new Worker(
|
|
||||||
new URL("./read_check_granular_worker.js", import.meta.url).href,
|
|
||||||
{
|
|
||||||
type: "module",
|
|
||||||
deno: {
|
|
||||||
namespace: true,
|
|
||||||
permissions: {
|
|
||||||
read: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let received = 0;
|
|
||||||
const messages = [];
|
|
||||||
|
|
||||||
worker.onmessage = ({ data: childResponse }) => {
|
|
||||||
received++;
|
|
||||||
postMessage({
|
|
||||||
childHasPermission: childResponse.hasPermission,
|
|
||||||
index: childResponse.index,
|
|
||||||
parentHasPermission: messages[childResponse.index],
|
|
||||||
});
|
|
||||||
if (received === messages.length) {
|
|
||||||
worker.terminate();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
onmessage = async ({ data }) => {
|
|
||||||
const { state } = await Deno.permissions.query({
|
|
||||||
name: "read",
|
|
||||||
path: data.path,
|
|
||||||
});
|
|
||||||
|
|
||||||
messages[data.index] = state === "granted";
|
|
||||||
|
|
||||||
worker.postMessage({
|
|
||||||
index: data.index,
|
|
||||||
route: data.route,
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,27 +1,18 @@
|
||||||
onmessage = async () => {
|
const worker = new Worker(
|
||||||
const { state } = await Deno.permissions.query({
|
new URL("./read_check_granular_worker.js", import.meta.url).href,
|
||||||
name: "read",
|
{
|
||||||
});
|
type: "module",
|
||||||
|
deno: {
|
||||||
const worker = new Worker(
|
namespace: true,
|
||||||
new URL("./read_check_worker.js", import.meta.url).href,
|
permissions: "none",
|
||||||
{
|
|
||||||
type: "module",
|
|
||||||
deno: {
|
|
||||||
namespace: true,
|
|
||||||
permissions: {
|
|
||||||
read: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
);
|
},
|
||||||
|
);
|
||||||
|
|
||||||
worker.onmessage = ({ data: childHasPermission }) => {
|
onmessage = ({ data }) => {
|
||||||
postMessage({
|
worker.postMessage(data);
|
||||||
parentHasPermission: state === "granted",
|
};
|
||||||
childHasPermission,
|
|
||||||
});
|
worker.onmessage = ({ data }) => {
|
||||||
close();
|
postMessage(data);
|
||||||
};
|
|
||||||
worker.postMessage(null);
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,11 +1,29 @@
|
||||||
onmessage = async ({ data }) => {
|
// deno-fmt-ignore-file
|
||||||
const { state } = await Deno.permissions.query({
|
postMessage({
|
||||||
name: "read",
|
envGlobal: (await Deno.permissions.query({ name: "env" })).state,
|
||||||
path: data.path,
|
envFoo: (await Deno.permissions.query({ name: "env", variable: "foo" })).state,
|
||||||
});
|
envAbsent: (await Deno.permissions.query({ name: "env", variable: "absent" })).state,
|
||||||
|
hrtime: (await Deno.permissions.query({ name: "hrtime" })).state,
|
||||||
postMessage({
|
netGlobal: (await Deno.permissions.query({ name: "net" })).state,
|
||||||
hasPermission: state === "granted",
|
netFoo: (await Deno.permissions.query({ name: "net", host: "foo" })).state,
|
||||||
index: data.index,
|
netFoo8000: (await Deno.permissions.query({ name: "net", host: "foo:8000" })).state,
|
||||||
});
|
netBar: (await Deno.permissions.query({ name: "net", host: "bar" })).state,
|
||||||
};
|
netBar8000: (await Deno.permissions.query({ name: "net", host: "bar:8000" })).state,
|
||||||
|
ffiGlobal: (await Deno.permissions.query({ name: "ffi" })).state,
|
||||||
|
ffiFoo: (await Deno.permissions.query({ name: "ffi", path: new URL("foo", import.meta.url) })).state,
|
||||||
|
ffiBar: (await Deno.permissions.query({ name: "ffi", path: "bar" })).state,
|
||||||
|
ffiAbsent: (await Deno.permissions.query({ name: "ffi", path: "absent" })).state,
|
||||||
|
readGlobal: (await Deno.permissions.query({ name: "read" })).state,
|
||||||
|
readFoo: (await Deno.permissions.query({ name: "read", path: new URL("foo", import.meta.url) })).state,
|
||||||
|
readBar: (await Deno.permissions.query({ name: "read", path: "bar" })).state,
|
||||||
|
readAbsent: (await Deno.permissions.query({ name: "read", path: "absent" })).state,
|
||||||
|
runGlobal: (await Deno.permissions.query({ name: "run" })).state,
|
||||||
|
runFoo: (await Deno.permissions.query({ name: "run", command: new URL("foo", import.meta.url) })).state,
|
||||||
|
runBar: (await Deno.permissions.query({ name: "run", command: "bar" })).state,
|
||||||
|
runBaz: (await Deno.permissions.query({ name: "run", command: "./baz" })).state,
|
||||||
|
runAbsent: (await Deno.permissions.query({ name: "run", command: "absent" })).state,
|
||||||
|
writeGlobal: (await Deno.permissions.query({ name: "write" })).state,
|
||||||
|
writeFoo: (await Deno.permissions.query({ name: "write", path: new URL("foo", import.meta.url) })).state,
|
||||||
|
writeBar: (await Deno.permissions.query({ name: "write", path: "bar" })).state,
|
||||||
|
writeAbsent: (await Deno.permissions.query({ name: "write", path: "absent" })).state,
|
||||||
|
});
|
||||||
|
|
235
cli/tests/testdata/workers/test.ts
vendored
235
cli/tests/testdata/workers/test.ts
vendored
|
@ -8,7 +8,6 @@ import {
|
||||||
assertThrows,
|
assertThrows,
|
||||||
} from "../../../../test_util/std/testing/asserts.ts";
|
} from "../../../../test_util/std/testing/asserts.ts";
|
||||||
import { deferred } from "../../../../test_util/std/async/deferred.ts";
|
import { deferred } from "../../../../test_util/std/async/deferred.ts";
|
||||||
import { fromFileUrl } from "../../../../test_util/std/path/mod.ts";
|
|
||||||
|
|
||||||
Deno.test({
|
Deno.test({
|
||||||
name: "worker terminate",
|
name: "worker terminate",
|
||||||
|
@ -454,7 +453,6 @@ Deno.test("Worker limit children permissions", async function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test("Worker limit children permissions granularly", async function () {
|
Deno.test("Worker limit children permissions granularly", async function () {
|
||||||
const promise = deferred();
|
|
||||||
const worker = new Worker(
|
const worker = new Worker(
|
||||||
new URL("./read_check_granular_worker.js", import.meta.url).href,
|
new URL("./read_check_granular_worker.js", import.meta.url).href,
|
||||||
{
|
{
|
||||||
|
@ -462,53 +460,52 @@ Deno.test("Worker limit children permissions granularly", async function () {
|
||||||
deno: {
|
deno: {
|
||||||
namespace: true,
|
namespace: true,
|
||||||
permissions: {
|
permissions: {
|
||||||
read: [
|
env: ["foo"],
|
||||||
new URL("./read_check_worker.js", import.meta.url),
|
hrtime: true,
|
||||||
],
|
net: ["foo", "bar:8000"],
|
||||||
|
ffi: [new URL("foo", import.meta.url), "bar"],
|
||||||
|
read: [new URL("foo", import.meta.url), "bar"],
|
||||||
|
run: [new URL("foo", import.meta.url), "bar", "./baz"],
|
||||||
|
write: [new URL("foo", import.meta.url), "bar"],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
const promise = deferred();
|
||||||
//Routes are relative to the spawned worker location
|
worker.onmessage = ({ data }) => promise.resolve(data);
|
||||||
const routes = [
|
assertEquals(await promise, {
|
||||||
{
|
envGlobal: "prompt",
|
||||||
permission: false,
|
envFoo: "granted",
|
||||||
path: fromFileUrl(
|
envAbsent: "prompt",
|
||||||
new URL("read_check_granular_worker.js", import.meta.url),
|
hrtime: "granted",
|
||||||
),
|
netGlobal: "prompt",
|
||||||
},
|
netFoo: "granted",
|
||||||
{
|
netFoo8000: "granted",
|
||||||
permission: true,
|
netBar: "prompt",
|
||||||
path: fromFileUrl(new URL("read_check_worker.js", import.meta.url)),
|
netBar8000: "granted",
|
||||||
},
|
ffiGlobal: "prompt",
|
||||||
];
|
ffiFoo: "granted",
|
||||||
|
ffiBar: "granted",
|
||||||
let checked = 0;
|
ffiAbsent: "prompt",
|
||||||
worker.onmessage = ({ data }) => {
|
readGlobal: "prompt",
|
||||||
checked++;
|
readFoo: "granted",
|
||||||
assertEquals(data.hasPermission, routes[data.index].permission);
|
readBar: "granted",
|
||||||
routes.shift();
|
readAbsent: "prompt",
|
||||||
if (checked === routes.length) {
|
runGlobal: "prompt",
|
||||||
promise.resolve();
|
runFoo: "granted",
|
||||||
}
|
runBar: "granted",
|
||||||
};
|
runBaz: "granted",
|
||||||
|
runAbsent: "prompt",
|
||||||
routes.forEach(({ path }, index) =>
|
writeGlobal: "prompt",
|
||||||
worker.postMessage({
|
writeFoo: "granted",
|
||||||
index,
|
writeBar: "granted",
|
||||||
path,
|
writeAbsent: "prompt",
|
||||||
})
|
});
|
||||||
);
|
|
||||||
|
|
||||||
await promise;
|
|
||||||
worker.terminate();
|
worker.terminate();
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test("Nested worker limit children permissions", async function () {
|
Deno.test("Nested worker limit children permissions", async function () {
|
||||||
const promise = deferred();
|
/** This worker has permissions but doesn't grant them to its children */
|
||||||
|
|
||||||
/** This worker has read permissions but doesn't grant them to its children */
|
|
||||||
const worker = new Worker(
|
const worker = new Worker(
|
||||||
new URL("./parent_read_check_worker.js", import.meta.url).href,
|
new URL("./parent_read_check_worker.js", import.meta.url).href,
|
||||||
{
|
{
|
||||||
|
@ -519,104 +516,65 @@ Deno.test("Nested worker limit children permissions", async function () {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
worker.onmessage = ({ data }) => {
|
|
||||||
assert(data.parentHasPermission);
|
|
||||||
assert(!data.childHasPermission);
|
|
||||||
promise.resolve();
|
|
||||||
};
|
|
||||||
|
|
||||||
worker.postMessage(null);
|
|
||||||
|
|
||||||
await promise;
|
|
||||||
worker.terminate();
|
|
||||||
});
|
|
||||||
|
|
||||||
Deno.test("Nested worker limit children permissions granularly", async function () {
|
|
||||||
const promise = deferred();
|
const promise = deferred();
|
||||||
|
worker.onmessage = ({ data }) => promise.resolve(data);
|
||||||
/** This worker has read permissions but doesn't grant them to its children */
|
assertEquals(await promise, {
|
||||||
const worker = new Worker(
|
envGlobal: "prompt",
|
||||||
new URL("./parent_read_check_granular_worker.js", import.meta.url)
|
envFoo: "prompt",
|
||||||
.href,
|
envAbsent: "prompt",
|
||||||
{
|
hrtime: "prompt",
|
||||||
type: "module",
|
netGlobal: "prompt",
|
||||||
deno: {
|
netFoo: "prompt",
|
||||||
namespace: true,
|
netFoo8000: "prompt",
|
||||||
permissions: {
|
netBar: "prompt",
|
||||||
read: [
|
netBar8000: "prompt",
|
||||||
new URL("./read_check_granular_worker.js", import.meta.url),
|
ffiGlobal: "prompt",
|
||||||
],
|
ffiFoo: "prompt",
|
||||||
},
|
ffiBar: "prompt",
|
||||||
},
|
ffiAbsent: "prompt",
|
||||||
},
|
readGlobal: "prompt",
|
||||||
);
|
readFoo: "prompt",
|
||||||
|
readBar: "prompt",
|
||||||
//Routes are relative to the spawned worker location
|
readAbsent: "prompt",
|
||||||
const routes = [
|
runGlobal: "prompt",
|
||||||
{
|
runFoo: "prompt",
|
||||||
childHasPermission: false,
|
runBar: "prompt",
|
||||||
parentHasPermission: true,
|
runBaz: "prompt",
|
||||||
path: fromFileUrl(
|
runAbsent: "prompt",
|
||||||
new URL("read_check_granular_worker.js", import.meta.url),
|
writeGlobal: "prompt",
|
||||||
),
|
writeFoo: "prompt",
|
||||||
},
|
writeBar: "prompt",
|
||||||
{
|
writeAbsent: "prompt",
|
||||||
childHasPermission: false,
|
});
|
||||||
parentHasPermission: false,
|
|
||||||
path: fromFileUrl(new URL("read_check_worker.js", import.meta.url)),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
let checked = 0;
|
|
||||||
worker.onmessage = ({ data }) => {
|
|
||||||
checked++;
|
|
||||||
assertEquals(
|
|
||||||
data.childHasPermission,
|
|
||||||
routes[data.index].childHasPermission,
|
|
||||||
);
|
|
||||||
assertEquals(
|
|
||||||
data.parentHasPermission,
|
|
||||||
routes[data.index].parentHasPermission,
|
|
||||||
);
|
|
||||||
if (checked === routes.length) {
|
|
||||||
promise.resolve();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Index needed cause requests will be handled asynchronously
|
|
||||||
routes.forEach(({ path }, index) =>
|
|
||||||
worker.postMessage({
|
|
||||||
index,
|
|
||||||
path,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
await promise;
|
|
||||||
worker.terminate();
|
worker.terminate();
|
||||||
});
|
});
|
||||||
|
|
||||||
// This test relies on env permissions not being granted on main thread
|
// This test relies on env permissions not being granted on main thread
|
||||||
Deno.test("Worker initialization throws on worker permissions greater than parent thread permissions", function () {
|
Deno.test({
|
||||||
assertThrows(
|
name:
|
||||||
() => {
|
"Worker initialization throws on worker permissions greater than parent thread permissions",
|
||||||
const worker = new Worker(
|
permissions: { env: false },
|
||||||
new URL("./deno_worker.ts", import.meta.url).href,
|
fn: function () {
|
||||||
{
|
assertThrows(
|
||||||
type: "module",
|
() => {
|
||||||
deno: {
|
const worker = new Worker(
|
||||||
namespace: true,
|
new URL("./deno_worker.ts", import.meta.url).href,
|
||||||
permissions: {
|
{
|
||||||
env: true,
|
type: "module",
|
||||||
|
deno: {
|
||||||
|
namespace: true,
|
||||||
|
permissions: {
|
||||||
|
env: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
);
|
||||||
);
|
worker.terminate();
|
||||||
worker.terminate();
|
},
|
||||||
},
|
Deno.errors.PermissionDenied,
|
||||||
Deno.errors.PermissionDenied,
|
"Can't escalate parent thread permissions",
|
||||||
"Can't escalate parent thread permissions",
|
);
|
||||||
);
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test("Worker with disabled permissions", async function () {
|
Deno.test("Worker with disabled permissions", async function () {
|
||||||
|
@ -643,6 +601,19 @@ Deno.test("Worker with disabled permissions", async function () {
|
||||||
worker.terminate();
|
worker.terminate();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Deno.test("Worker with invalid permission arg", function () {
|
||||||
|
assertThrows(
|
||||||
|
() =>
|
||||||
|
new Worker(`data:,close();`, {
|
||||||
|
type: "module",
|
||||||
|
// @ts-expect-error invalid env value
|
||||||
|
deno: { permissions: { env: "foo" } },
|
||||||
|
}),
|
||||||
|
TypeError,
|
||||||
|
'Error parsing args: (deno.permissions.env) invalid value: string "foo", expected "inherit" or boolean or string[]',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
Deno.test({
|
Deno.test({
|
||||||
name: "worker location",
|
name: "worker location",
|
||||||
fn: async function () {
|
fn: async function () {
|
||||||
|
|
|
@ -20,6 +20,8 @@ use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct Unstable(pub bool);
|
pub struct Unstable(pub bool);
|
||||||
|
@ -37,7 +39,7 @@ fn check_unstable(state: &OpState, api_name: &str) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait FfiPermissions {
|
pub trait FfiPermissions {
|
||||||
fn check(&mut self, path: &str) -> Result<(), AnyError>;
|
fn check(&mut self, path: &Path) -> Result<(), AnyError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -366,7 +368,7 @@ where
|
||||||
|
|
||||||
check_unstable(state, "Deno.dlopen");
|
check_unstable(state, "Deno.dlopen");
|
||||||
let permissions = state.borrow_mut::<FP>();
|
let permissions = state.borrow_mut::<FP>();
|
||||||
permissions.check(&path)?;
|
permissions.check(&PathBuf::from(&path))?;
|
||||||
|
|
||||||
let lib = Library::open(&path).map_err(|e| {
|
let lib = Library::open(&path).map_err(|e| {
|
||||||
dlopen::Error::OpeningLibraryError(std::io::Error::new(
|
dlopen::Error::OpeningLibraryError(std::io::Error::new(
|
||||||
|
|
|
@ -83,7 +83,10 @@ mod not_docs {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl deno_ffi::FfiPermissions for Permissions {
|
impl deno_ffi::FfiPermissions for Permissions {
|
||||||
fn check(&mut self, _path: &str) -> Result<(), deno_core::error::AnyError> {
|
fn check(
|
||||||
|
&mut self,
|
||||||
|
_path: &Path,
|
||||||
|
) -> Result<(), deno_core::error::AnyError> {
|
||||||
unreachable!("snapshotting!")
|
unreachable!("snapshotting!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,10 @@
|
||||||
} = window;
|
} = window;
|
||||||
const { pathFromURL } = window.__bootstrap.util;
|
const { pathFromURL } = window.__bootstrap.util;
|
||||||
const {
|
const {
|
||||||
|
ArrayIsArray,
|
||||||
ArrayPrototypeIncludes,
|
ArrayPrototypeIncludes,
|
||||||
|
ArrayPrototypeMap,
|
||||||
|
ArrayPrototypeSlice,
|
||||||
Map,
|
Map,
|
||||||
MapPrototypeGet,
|
MapPrototypeGet,
|
||||||
MapPrototypeHas,
|
MapPrototypeHas,
|
||||||
|
@ -162,7 +165,9 @@
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (desc.name === "read" || desc.name === "write") {
|
if (
|
||||||
|
desc.name === "read" || desc.name === "write" || desc.name === "ffi"
|
||||||
|
) {
|
||||||
desc.path = pathFromURL(desc.path);
|
desc.path = pathFromURL(desc.path);
|
||||||
} else if (desc.name === "run") {
|
} else if (desc.name === "run") {
|
||||||
desc.command = pathFromURL(desc.command);
|
desc.command = pathFromURL(desc.command);
|
||||||
|
@ -213,7 +218,34 @@
|
||||||
|
|
||||||
const permissions = new Permissions(illegalConstructorKey);
|
const permissions = new Permissions(illegalConstructorKey);
|
||||||
|
|
||||||
|
/** Converts all file URLs in FS allowlists to paths. */
|
||||||
|
function serializePermissions(permissions) {
|
||||||
|
if (typeof permissions == "object" && permissions != null) {
|
||||||
|
const serializedPermissions = {};
|
||||||
|
for (const key of ["read", "write", "run", "ffi"]) {
|
||||||
|
if (ArrayIsArray(permissions[key])) {
|
||||||
|
serializedPermissions[key] = ArrayPrototypeMap(
|
||||||
|
permissions[key],
|
||||||
|
(path) => pathFromURL(path),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
serializedPermissions[key] = permissions[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const key of ["env", "hrtime", "net"]) {
|
||||||
|
if (ArrayIsArray(permissions[key])) {
|
||||||
|
serializedPermissions[key] = ArrayPrototypeSlice(permissions[key]);
|
||||||
|
} else {
|
||||||
|
serializedPermissions[key] = permissions[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return serializedPermissions;
|
||||||
|
}
|
||||||
|
return permissions;
|
||||||
|
}
|
||||||
|
|
||||||
window.__bootstrap.permissions = {
|
window.__bootstrap.permissions = {
|
||||||
|
serializePermissions,
|
||||||
permissions,
|
permissions,
|
||||||
Permissions,
|
Permissions,
|
||||||
PermissionStatus,
|
PermissionStatus,
|
|
@ -4,8 +4,6 @@
|
||||||
((window) => {
|
((window) => {
|
||||||
const core = window.Deno.core;
|
const core = window.Deno.core;
|
||||||
const {
|
const {
|
||||||
ArrayIsArray,
|
|
||||||
ArrayPrototypeMap,
|
|
||||||
Error,
|
Error,
|
||||||
StringPrototypeStartsWith,
|
StringPrototypeStartsWith,
|
||||||
String,
|
String,
|
||||||
|
@ -15,7 +13,8 @@
|
||||||
const webidl = window.__bootstrap.webidl;
|
const webidl = window.__bootstrap.webidl;
|
||||||
const { URL } = window.__bootstrap.url;
|
const { URL } = window.__bootstrap.url;
|
||||||
const { getLocationHref } = window.__bootstrap.location;
|
const { getLocationHref } = window.__bootstrap.location;
|
||||||
const { log, pathFromURL } = window.__bootstrap.util;
|
const { serializePermissions } = window.__bootstrap.permissions;
|
||||||
|
const { log } = window.__bootstrap.util;
|
||||||
const { defineEventHandler } = window.__bootstrap.event;
|
const { defineEventHandler } = window.__bootstrap.event;
|
||||||
const { deserializeJsMessageData, serializeJsMessageData } =
|
const { deserializeJsMessageData, serializeJsMessageData } =
|
||||||
window.__bootstrap.messagePort;
|
window.__bootstrap.messagePort;
|
||||||
|
@ -32,7 +31,7 @@
|
||||||
return core.opSync("op_create_worker", {
|
return core.opSync("op_create_worker", {
|
||||||
hasSourceCode,
|
hasSourceCode,
|
||||||
name,
|
name,
|
||||||
permissions,
|
permissions: serializePermissions(permissions),
|
||||||
sourceCode,
|
sourceCode,
|
||||||
specifier,
|
specifier,
|
||||||
useDenoNamespace,
|
useDenoNamespace,
|
||||||
|
@ -56,87 +55,6 @@
|
||||||
return core.opAsync("op_host_recv_message", id);
|
return core.opAsync("op_host_recv_message", id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {"inherit" | boolean} value
|
|
||||||
* @param {string} permission
|
|
||||||
* @return {boolean}
|
|
||||||
*/
|
|
||||||
function parseUnitPermission(
|
|
||||||
value,
|
|
||||||
permission,
|
|
||||||
) {
|
|
||||||
if (value !== "inherit" && typeof value !== "boolean") {
|
|
||||||
throw new Error(
|
|
||||||
`Expected 'boolean' for ${permission} permission, ${typeof value} received`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return value === "inherit" ? undefined : value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} permission
|
|
||||||
* @return {(boolean | string[])}
|
|
||||||
*/
|
|
||||||
function parseArrayPermission(
|
|
||||||
value,
|
|
||||||
permission,
|
|
||||||
) {
|
|
||||||
if (typeof value === "string") {
|
|
||||||
if (value !== "inherit") {
|
|
||||||
throw new Error(
|
|
||||||
`Expected 'array' or 'boolean' for ${permission} permission, "${value}" received`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if (!ArrayIsArray(value) && typeof value !== "boolean") {
|
|
||||||
throw new Error(
|
|
||||||
`Expected 'array' or 'boolean' for ${permission} permission, ${typeof value} received`,
|
|
||||||
);
|
|
||||||
//Casts URLs to absolute routes
|
|
||||||
} else if (ArrayIsArray(value)) {
|
|
||||||
value = ArrayPrototypeMap(value, (route) => {
|
|
||||||
if (route instanceof URL) {
|
|
||||||
if (permission === "net") {
|
|
||||||
throw new Error(
|
|
||||||
`Expected 'string' for net permission, received 'URL'`,
|
|
||||||
);
|
|
||||||
} else if (permission === "env") {
|
|
||||||
throw new Error(
|
|
||||||
`Expected 'string' for env permission, received 'URL'`,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
route = pathFromURL(route);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return route;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return value === "inherit" ? undefined : value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Normalizes data, runs checks on parameters and deletes inherited permissions
|
|
||||||
*/
|
|
||||||
function parsePermissions({
|
|
||||||
env = "inherit",
|
|
||||||
hrtime = "inherit",
|
|
||||||
net = "inherit",
|
|
||||||
ffi = "inherit",
|
|
||||||
read = "inherit",
|
|
||||||
run = "inherit",
|
|
||||||
write = "inherit",
|
|
||||||
}) {
|
|
||||||
return {
|
|
||||||
env: parseArrayPermission(env, "env"),
|
|
||||||
hrtime: parseUnitPermission(hrtime, "hrtime"),
|
|
||||||
net: parseArrayPermission(net, "net"),
|
|
||||||
ffi: parseUnitPermission(ffi, "ffi"),
|
|
||||||
read: parseArrayPermission(read, "read"),
|
|
||||||
run: parseUnitPermission(run, "run"),
|
|
||||||
write: parseArrayPermission(write, "write"),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
class Worker extends EventTarget {
|
class Worker extends EventTarget {
|
||||||
#id = 0;
|
#id = 0;
|
||||||
#name = "";
|
#name = "";
|
||||||
|
@ -152,43 +70,23 @@
|
||||||
super();
|
super();
|
||||||
specifier = String(specifier);
|
specifier = String(specifier);
|
||||||
const {
|
const {
|
||||||
deno = {},
|
deno,
|
||||||
name = "unknown",
|
name,
|
||||||
type = "classic",
|
type = "classic",
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
// TODO(Soremwar)
|
let namespace;
|
||||||
// `deno: boolean` is kept for backwards compatibility with the previous
|
let permissions;
|
||||||
// worker options implementation. Remove for 2.0
|
if (typeof deno == "object") {
|
||||||
let workerDenoAttributes;
|
namespace = deno.namespace ?? false;
|
||||||
if (typeof deno == "boolean") {
|
permissions = deno.permissions ?? undefined;
|
||||||
workerDenoAttributes = {
|
|
||||||
// Change this to enable the Deno namespace by default
|
|
||||||
namespace: deno,
|
|
||||||
permissions: null,
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
workerDenoAttributes = {
|
// Assume `deno: boolean | undefined`.
|
||||||
// Change this to enable the Deno namespace by default
|
// TODO(Soremwar)
|
||||||
namespace: !!(deno?.namespace ?? false),
|
// `deno: boolean` is kept for backwards compatibility with the previous
|
||||||
permissions: (deno?.permissions ?? "inherit") === "inherit"
|
// worker options implementation. Remove for 2.0
|
||||||
? null
|
namespace = !!deno;
|
||||||
: deno?.permissions,
|
permissions = undefined;
|
||||||
};
|
|
||||||
|
|
||||||
// If the permission option is set to "none", all permissions
|
|
||||||
// must be removed from the worker
|
|
||||||
if (workerDenoAttributes.permissions === "none") {
|
|
||||||
workerDenoAttributes.permissions = {
|
|
||||||
env: false,
|
|
||||||
hrtime: false,
|
|
||||||
net: false,
|
|
||||||
ffi: false,
|
|
||||||
read: false,
|
|
||||||
run: false,
|
|
||||||
write: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const workerType = webidl.converters["WorkerType"](type);
|
const workerType = webidl.converters["WorkerType"](type);
|
||||||
|
@ -218,17 +116,16 @@
|
||||||
specifier,
|
specifier,
|
||||||
hasSourceCode,
|
hasSourceCode,
|
||||||
sourceCode,
|
sourceCode,
|
||||||
workerDenoAttributes.namespace,
|
namespace,
|
||||||
workerDenoAttributes.permissions === null
|
permissions,
|
||||||
? null
|
name,
|
||||||
: parsePermissions(workerDenoAttributes.permissions),
|
|
||||||
options?.name,
|
|
||||||
workerType,
|
workerType,
|
||||||
);
|
);
|
||||||
this.#id = id;
|
this.#id = id;
|
||||||
this.#pollControl();
|
this.#pollControl();
|
||||||
this.#pollMessages();
|
this.#pollMessages();
|
||||||
}
|
}
|
||||||
|
|
||||||
#handleError(e) {
|
#handleError(e) {
|
||||||
const event = new ErrorEvent("error", {
|
const event = new ErrorEvent("error", {
|
||||||
cancelable: true,
|
cancelable: true,
|
||||||
|
@ -359,7 +256,6 @@
|
||||||
]);
|
]);
|
||||||
|
|
||||||
window.__bootstrap.worker = {
|
window.__bootstrap.worker = {
|
||||||
parsePermissions,
|
|
||||||
Worker,
|
Worker,
|
||||||
};
|
};
|
||||||
})(this);
|
})(this);
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
|
|
||||||
((window) => {
|
((window) => {
|
||||||
const core = window.Deno.core;
|
const core = window.Deno.core;
|
||||||
const { parsePermissions } = window.__bootstrap.worker;
|
|
||||||
const { setExitHandler } = window.__bootstrap.os;
|
const { setExitHandler } = window.__bootstrap.os;
|
||||||
const { Console, inspectArgs } = window.__bootstrap.console;
|
const { Console, inspectArgs } = window.__bootstrap.console;
|
||||||
const { metrics } = core;
|
const { metrics } = core;
|
||||||
|
const { serializePermissions } = window.__bootstrap.permissions;
|
||||||
const { assert } = window.__bootstrap.util;
|
const { assert } = window.__bootstrap.util;
|
||||||
const {
|
const {
|
||||||
ArrayPrototypeFilter,
|
ArrayPrototypeFilter,
|
||||||
|
@ -230,7 +230,7 @@ finishing test case.`;
|
||||||
function pledgePermissions(permissions) {
|
function pledgePermissions(permissions) {
|
||||||
return core.opSync(
|
return core.opSync(
|
||||||
"op_pledge_test_permissions",
|
"op_pledge_test_permissions",
|
||||||
parsePermissions(permissions),
|
serializePermissions(permissions),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,7 +289,7 @@ finishing test case.`;
|
||||||
if (testDef.permissions) {
|
if (testDef.permissions) {
|
||||||
testDef.fn = withPermissions(
|
testDef.fn = withPermissions(
|
||||||
testDef.fn,
|
testDef.fn,
|
||||||
parsePermissions(testDef.permissions),
|
testDef.permissions,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ pub struct PermissionArgs {
|
||||||
host: Option<String>,
|
host: Option<String>,
|
||||||
variable: Option<String>,
|
variable: Option<String>,
|
||||||
command: Option<String>,
|
command: Option<String>,
|
||||||
library: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn op_query_permission(
|
pub fn op_query_permission(
|
||||||
|
@ -50,7 +49,7 @@ pub fn op_query_permission(
|
||||||
),
|
),
|
||||||
"env" => permissions.env.query(args.variable.as_deref()),
|
"env" => permissions.env.query(args.variable.as_deref()),
|
||||||
"run" => permissions.run.query(args.command.as_deref()),
|
"run" => permissions.run.query(args.command.as_deref()),
|
||||||
"ffi" => permissions.ffi.query(args.library.as_deref()),
|
"ffi" => permissions.ffi.query(args.path.as_deref().map(Path::new)),
|
||||||
"hrtime" => permissions.hrtime.query(),
|
"hrtime" => permissions.hrtime.query(),
|
||||||
n => {
|
n => {
|
||||||
return Err(custom_error(
|
return Err(custom_error(
|
||||||
|
@ -81,7 +80,7 @@ pub fn op_revoke_permission(
|
||||||
),
|
),
|
||||||
"env" => permissions.env.revoke(args.variable.as_deref()),
|
"env" => permissions.env.revoke(args.variable.as_deref()),
|
||||||
"run" => permissions.run.revoke(args.command.as_deref()),
|
"run" => permissions.run.revoke(args.command.as_deref()),
|
||||||
"ffi" => permissions.ffi.revoke(args.library.as_deref()),
|
"ffi" => permissions.ffi.revoke(args.path.as_deref().map(Path::new)),
|
||||||
"hrtime" => permissions.hrtime.revoke(),
|
"hrtime" => permissions.hrtime.revoke(),
|
||||||
n => {
|
n => {
|
||||||
return Err(custom_error(
|
return Err(custom_error(
|
||||||
|
@ -112,7 +111,7 @@ pub fn op_request_permission(
|
||||||
),
|
),
|
||||||
"env" => permissions.env.request(args.variable.as_deref()),
|
"env" => permissions.env.request(args.variable.as_deref()),
|
||||||
"run" => permissions.run.request(args.command.as_deref()),
|
"run" => permissions.run.request(args.command.as_deref()),
|
||||||
"ffi" => permissions.ffi.request(args.library.as_deref()),
|
"ffi" => permissions.ffi.request(args.path.as_deref().map(Path::new)),
|
||||||
"hrtime" => permissions.hrtime.request(),
|
"hrtime" => permissions.hrtime.request(),
|
||||||
n => {
|
n => {
|
||||||
return Err(custom_error(
|
return Err(custom_error(
|
||||||
|
|
|
@ -1,18 +1,9 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
use crate::ops::TestingFeaturesEnabled;
|
use crate::ops::TestingFeaturesEnabled;
|
||||||
use crate::permissions::resolve_read_allowlist;
|
use crate::permissions::create_child_permissions;
|
||||||
use crate::permissions::resolve_write_allowlist;
|
use crate::permissions::ChildPermissionsArg;
|
||||||
use crate::permissions::EnvDescriptor;
|
|
||||||
use crate::permissions::FfiDescriptor;
|
|
||||||
use crate::permissions::NetDescriptor;
|
|
||||||
use crate::permissions::PermissionState;
|
|
||||||
use crate::permissions::Permissions;
|
use crate::permissions::Permissions;
|
||||||
use crate::permissions::ReadDescriptor;
|
|
||||||
use crate::permissions::RunDescriptor;
|
|
||||||
use crate::permissions::UnaryPermission;
|
|
||||||
use crate::permissions::UnitPermission;
|
|
||||||
use crate::permissions::WriteDescriptor;
|
|
||||||
use crate::web_worker::run_web_worker;
|
use crate::web_worker::run_web_worker;
|
||||||
use crate::web_worker::SendableWebWorkerHandle;
|
use crate::web_worker::SendableWebWorkerHandle;
|
||||||
use crate::web_worker::WebWorker;
|
use crate::web_worker::WebWorker;
|
||||||
|
@ -20,14 +11,10 @@ use crate::web_worker::WebWorkerHandle;
|
||||||
use crate::web_worker::WebWorkerType;
|
use crate::web_worker::WebWorkerType;
|
||||||
use crate::web_worker::WorkerControlEvent;
|
use crate::web_worker::WorkerControlEvent;
|
||||||
use crate::web_worker::WorkerId;
|
use crate::web_worker::WorkerId;
|
||||||
use deno_core::error::custom_error;
|
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
use deno_core::op_async;
|
use deno_core::op_async;
|
||||||
use deno_core::op_sync;
|
use deno_core::op_sync;
|
||||||
use deno_core::serde::de;
|
|
||||||
use deno_core::serde::de::SeqAccess;
|
|
||||||
use deno_core::serde::Deserialize;
|
use deno_core::serde::Deserialize;
|
||||||
use deno_core::serde::Deserializer;
|
|
||||||
use deno_core::Extension;
|
use deno_core::Extension;
|
||||||
use deno_core::ModuleSpecifier;
|
use deno_core::ModuleSpecifier;
|
||||||
use deno_core::OpState;
|
use deno_core::OpState;
|
||||||
|
@ -35,10 +22,6 @@ use deno_web::JsMessageData;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::HashSet;
|
|
||||||
use std::convert::From;
|
|
||||||
use std::fmt;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::thread::JoinHandle;
|
use std::thread::JoinHandle;
|
||||||
|
@ -131,369 +114,12 @@ pub fn init(create_web_worker_cb: Arc<CreateWebWorkerCb>) -> Extension {
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn merge_boolean_permission(
|
|
||||||
mut main: UnitPermission,
|
|
||||||
worker: Option<PermissionState>,
|
|
||||||
) -> Result<UnitPermission, AnyError> {
|
|
||||||
if let Some(worker) = worker {
|
|
||||||
if worker < main.state {
|
|
||||||
return Err(custom_error(
|
|
||||||
"PermissionDenied",
|
|
||||||
"Can't escalate parent thread permissions",
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
main.state = worker;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(main)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn merge_net_permission(
|
|
||||||
mut main: UnaryPermission<NetDescriptor>,
|
|
||||||
worker: Option<UnaryPermission<NetDescriptor>>,
|
|
||||||
) -> Result<UnaryPermission<NetDescriptor>, AnyError> {
|
|
||||||
if let Some(worker) = worker {
|
|
||||||
if (worker.global_state < main.global_state)
|
|
||||||
|| !worker
|
|
||||||
.granted_list
|
|
||||||
.iter()
|
|
||||||
.all(|x| main.check(&(&x.0, x.1)).is_ok())
|
|
||||||
{
|
|
||||||
return Err(custom_error(
|
|
||||||
"PermissionDenied",
|
|
||||||
"Can't escalate parent thread permissions",
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
main.global_state = worker.global_state;
|
|
||||||
main.granted_list = worker.granted_list;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(main)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn merge_read_permission(
|
|
||||||
mut main: UnaryPermission<ReadDescriptor>,
|
|
||||||
worker: Option<UnaryPermission<ReadDescriptor>>,
|
|
||||||
) -> Result<UnaryPermission<ReadDescriptor>, AnyError> {
|
|
||||||
if let Some(worker) = worker {
|
|
||||||
if (worker.global_state < main.global_state)
|
|
||||||
|| !worker
|
|
||||||
.granted_list
|
|
||||||
.iter()
|
|
||||||
.all(|x| main.check(x.0.as_path()).is_ok())
|
|
||||||
{
|
|
||||||
return Err(custom_error(
|
|
||||||
"PermissionDenied",
|
|
||||||
"Can't escalate parent thread permissions",
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
main.global_state = worker.global_state;
|
|
||||||
main.granted_list = worker.granted_list;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(main)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn merge_write_permission(
|
|
||||||
mut main: UnaryPermission<WriteDescriptor>,
|
|
||||||
worker: Option<UnaryPermission<WriteDescriptor>>,
|
|
||||||
) -> Result<UnaryPermission<WriteDescriptor>, AnyError> {
|
|
||||||
if let Some(worker) = worker {
|
|
||||||
if (worker.global_state < main.global_state)
|
|
||||||
|| !worker
|
|
||||||
.granted_list
|
|
||||||
.iter()
|
|
||||||
.all(|x| main.check(x.0.as_path()).is_ok())
|
|
||||||
{
|
|
||||||
return Err(custom_error(
|
|
||||||
"PermissionDenied",
|
|
||||||
"Can't escalate parent thread permissions",
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
main.global_state = worker.global_state;
|
|
||||||
main.granted_list = worker.granted_list;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(main)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn merge_env_permission(
|
|
||||||
mut main: UnaryPermission<EnvDescriptor>,
|
|
||||||
worker: Option<UnaryPermission<EnvDescriptor>>,
|
|
||||||
) -> Result<UnaryPermission<EnvDescriptor>, AnyError> {
|
|
||||||
if let Some(worker) = worker {
|
|
||||||
if (worker.global_state < main.global_state)
|
|
||||||
|| !worker
|
|
||||||
.granted_list
|
|
||||||
.iter()
|
|
||||||
.all(|x| main.check(x.as_ref()).is_ok())
|
|
||||||
{
|
|
||||||
return Err(custom_error(
|
|
||||||
"PermissionDenied",
|
|
||||||
"Can't escalate parent thread permissions",
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
main.global_state = worker.global_state;
|
|
||||||
main.granted_list = worker.granted_list;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(main)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn merge_run_permission(
|
|
||||||
mut main: UnaryPermission<RunDescriptor>,
|
|
||||||
worker: Option<UnaryPermission<RunDescriptor>>,
|
|
||||||
) -> Result<UnaryPermission<RunDescriptor>, AnyError> {
|
|
||||||
if let Some(worker) = worker {
|
|
||||||
if (worker.global_state < main.global_state)
|
|
||||||
|| !worker.granted_list.iter().all(|x| main.check(&x.0).is_ok())
|
|
||||||
{
|
|
||||||
return Err(custom_error(
|
|
||||||
"PermissionDenied",
|
|
||||||
"Can't escalate parent thread permissions",
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
main.global_state = worker.global_state;
|
|
||||||
main.granted_list = worker.granted_list;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(main)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn merge_ffi_permission(
|
|
||||||
mut main: UnaryPermission<FfiDescriptor>,
|
|
||||||
worker: Option<UnaryPermission<FfiDescriptor>>,
|
|
||||||
) -> Result<UnaryPermission<FfiDescriptor>, AnyError> {
|
|
||||||
if let Some(worker) = worker {
|
|
||||||
if (worker.global_state < main.global_state)
|
|
||||||
|| !worker.granted_list.iter().all(|x| main.check(&x.0).is_ok())
|
|
||||||
{
|
|
||||||
return Err(custom_error(
|
|
||||||
"PermissionDenied",
|
|
||||||
"Can't escalate parent thread permissions",
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
main.global_state = worker.global_state;
|
|
||||||
main.granted_list = worker.granted_list;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(main)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_worker_permissions(
|
|
||||||
main_perms: Permissions,
|
|
||||||
worker_perms: PermissionsArg,
|
|
||||||
) -> Result<Permissions, AnyError> {
|
|
||||||
Ok(Permissions {
|
|
||||||
env: merge_env_permission(main_perms.env, worker_perms.env)?,
|
|
||||||
hrtime: merge_boolean_permission(main_perms.hrtime, worker_perms.hrtime)?,
|
|
||||||
net: merge_net_permission(main_perms.net, worker_perms.net)?,
|
|
||||||
ffi: merge_ffi_permission(main_perms.ffi, worker_perms.ffi)?,
|
|
||||||
read: merge_read_permission(main_perms.read, worker_perms.read)?,
|
|
||||||
run: merge_run_permission(main_perms.run, worker_perms.run)?,
|
|
||||||
write: merge_write_permission(main_perms.write, worker_perms.write)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub struct PermissionsArg {
|
|
||||||
#[serde(default, deserialize_with = "as_unary_env_permission")]
|
|
||||||
env: Option<UnaryPermission<EnvDescriptor>>,
|
|
||||||
#[serde(default, deserialize_with = "as_permission_state")]
|
|
||||||
hrtime: Option<PermissionState>,
|
|
||||||
#[serde(default, deserialize_with = "as_unary_net_permission")]
|
|
||||||
net: Option<UnaryPermission<NetDescriptor>>,
|
|
||||||
#[serde(default, deserialize_with = "as_unary_ffi_permission")]
|
|
||||||
ffi: Option<UnaryPermission<FfiDescriptor>>,
|
|
||||||
#[serde(default, deserialize_with = "as_unary_read_permission")]
|
|
||||||
read: Option<UnaryPermission<ReadDescriptor>>,
|
|
||||||
#[serde(default, deserialize_with = "as_unary_run_permission")]
|
|
||||||
run: Option<UnaryPermission<RunDescriptor>>,
|
|
||||||
#[serde(default, deserialize_with = "as_unary_write_permission")]
|
|
||||||
write: Option<UnaryPermission<WriteDescriptor>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_permission_state<'de, D>(
|
|
||||||
deserializer: D,
|
|
||||||
) -> Result<Option<PermissionState>, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
let value: bool = Deserialize::deserialize(deserializer)?;
|
|
||||||
|
|
||||||
match value {
|
|
||||||
true => Ok(Some(PermissionState::Granted)),
|
|
||||||
false => Ok(Some(PermissionState::Denied)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UnaryPermissionBase {
|
|
||||||
global_state: PermissionState,
|
|
||||||
paths: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ParseBooleanOrStringVec;
|
|
||||||
|
|
||||||
impl<'de> de::Visitor<'de> for ParseBooleanOrStringVec {
|
|
||||||
type Value = UnaryPermissionBase;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
formatter.write_str("a vector of strings or a boolean")
|
|
||||||
}
|
|
||||||
|
|
||||||
// visit_unit maps undefined/missing values to false
|
|
||||||
fn visit_unit<E>(self) -> Result<UnaryPermissionBase, E>
|
|
||||||
where
|
|
||||||
E: de::Error,
|
|
||||||
{
|
|
||||||
self.visit_bool(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_bool<E>(self, v: bool) -> Result<UnaryPermissionBase, E>
|
|
||||||
where
|
|
||||||
E: de::Error,
|
|
||||||
{
|
|
||||||
Ok(UnaryPermissionBase {
|
|
||||||
global_state: match v {
|
|
||||||
true => PermissionState::Granted,
|
|
||||||
false => PermissionState::Denied,
|
|
||||||
},
|
|
||||||
paths: Vec::new(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_seq<V>(self, mut visitor: V) -> Result<UnaryPermissionBase, V::Error>
|
|
||||||
where
|
|
||||||
V: SeqAccess<'de>,
|
|
||||||
{
|
|
||||||
let mut vec: Vec<String> = Vec::new();
|
|
||||||
|
|
||||||
let mut value = visitor.next_element::<String>()?;
|
|
||||||
while value.is_some() {
|
|
||||||
vec.push(value.unwrap());
|
|
||||||
value = visitor.next_element()?;
|
|
||||||
}
|
|
||||||
Ok(UnaryPermissionBase {
|
|
||||||
global_state: PermissionState::Prompt,
|
|
||||||
paths: vec,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_unary_net_permission<'de, D>(
|
|
||||||
deserializer: D,
|
|
||||||
) -> Result<Option<UnaryPermission<NetDescriptor>>, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
let value: UnaryPermissionBase =
|
|
||||||
deserializer.deserialize_any(ParseBooleanOrStringVec)?;
|
|
||||||
|
|
||||||
let allowed: HashSet<NetDescriptor> = value
|
|
||||||
.paths
|
|
||||||
.into_iter()
|
|
||||||
.map(NetDescriptor::from_string)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Ok(Some(UnaryPermission::<NetDescriptor> {
|
|
||||||
global_state: value.global_state,
|
|
||||||
granted_list: allowed,
|
|
||||||
..Default::default()
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_unary_read_permission<'de, D>(
|
|
||||||
deserializer: D,
|
|
||||||
) -> Result<Option<UnaryPermission<ReadDescriptor>>, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
let value: UnaryPermissionBase =
|
|
||||||
deserializer.deserialize_any(ParseBooleanOrStringVec)?;
|
|
||||||
|
|
||||||
let paths: Vec<PathBuf> =
|
|
||||||
value.paths.into_iter().map(PathBuf::from).collect();
|
|
||||||
|
|
||||||
Ok(Some(UnaryPermission::<ReadDescriptor> {
|
|
||||||
global_state: value.global_state,
|
|
||||||
granted_list: resolve_read_allowlist(&Some(paths)),
|
|
||||||
..Default::default()
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_unary_write_permission<'de, D>(
|
|
||||||
deserializer: D,
|
|
||||||
) -> Result<Option<UnaryPermission<WriteDescriptor>>, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
let value: UnaryPermissionBase =
|
|
||||||
deserializer.deserialize_any(ParseBooleanOrStringVec)?;
|
|
||||||
|
|
||||||
let paths: Vec<PathBuf> =
|
|
||||||
value.paths.into_iter().map(PathBuf::from).collect();
|
|
||||||
|
|
||||||
Ok(Some(UnaryPermission::<WriteDescriptor> {
|
|
||||||
global_state: value.global_state,
|
|
||||||
granted_list: resolve_write_allowlist(&Some(paths)),
|
|
||||||
..Default::default()
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_unary_env_permission<'de, D>(
|
|
||||||
deserializer: D,
|
|
||||||
) -> Result<Option<UnaryPermission<EnvDescriptor>>, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
let value: UnaryPermissionBase =
|
|
||||||
deserializer.deserialize_any(ParseBooleanOrStringVec)?;
|
|
||||||
|
|
||||||
Ok(Some(UnaryPermission::<EnvDescriptor> {
|
|
||||||
global_state: value.global_state,
|
|
||||||
granted_list: value.paths.into_iter().map(EnvDescriptor::new).collect(),
|
|
||||||
..Default::default()
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_unary_run_permission<'de, D>(
|
|
||||||
deserializer: D,
|
|
||||||
) -> Result<Option<UnaryPermission<RunDescriptor>>, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
let value: UnaryPermissionBase =
|
|
||||||
deserializer.deserialize_any(ParseBooleanOrStringVec)?;
|
|
||||||
|
|
||||||
Ok(Some(UnaryPermission::<RunDescriptor> {
|
|
||||||
global_state: value.global_state,
|
|
||||||
granted_list: value.paths.into_iter().map(RunDescriptor).collect(),
|
|
||||||
..Default::default()
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_unary_ffi_permission<'de, D>(
|
|
||||||
deserializer: D,
|
|
||||||
) -> Result<Option<UnaryPermission<FfiDescriptor>>, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
let value: UnaryPermissionBase =
|
|
||||||
deserializer.deserialize_any(ParseBooleanOrStringVec)?;
|
|
||||||
|
|
||||||
Ok(Some(UnaryPermission::<FfiDescriptor> {
|
|
||||||
global_state: value.global_state,
|
|
||||||
granted_list: value.paths.into_iter().map(FfiDescriptor).collect(),
|
|
||||||
..Default::default()
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct CreateWorkerArgs {
|
pub struct CreateWorkerArgs {
|
||||||
has_source_code: bool,
|
has_source_code: bool,
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
permissions: Option<PermissionsArg>,
|
permissions: Option<ChildPermissionsArg>,
|
||||||
source_code: String,
|
source_code: String,
|
||||||
specifier: String,
|
specifier: String,
|
||||||
use_deno_namespace: bool,
|
use_deno_namespace: bool,
|
||||||
|
@ -528,13 +154,18 @@ fn op_create_worker(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let parent_permissions = state.borrow::<Permissions>().clone();
|
|
||||||
let worker_permissions = if let Some(permissions) = args.permissions {
|
if args.permissions.is_some() {
|
||||||
super::check_unstable(state, "Worker.deno.permissions");
|
super::check_unstable(state, "Worker.deno.permissions");
|
||||||
create_worker_permissions(parent_permissions.clone(), permissions)?
|
}
|
||||||
|
let parent_permissions = state.borrow_mut::<Permissions>();
|
||||||
|
let worker_permissions = if let Some(child_permissions_arg) = args.permissions
|
||||||
|
{
|
||||||
|
create_child_permissions(parent_permissions, child_permissions_arg)?
|
||||||
} else {
|
} else {
|
||||||
parent_permissions.clone()
|
parent_permissions.clone()
|
||||||
};
|
};
|
||||||
|
let parent_permissions = parent_permissions.clone();
|
||||||
|
|
||||||
let worker_id = state.take::<WorkerId>();
|
let worker_id = state.take::<WorkerId>();
|
||||||
let create_module_loader = state.take::<CreateWebWorkerCbHolder>();
|
let create_module_loader = state.take::<CreateWebWorkerCbHolder>();
|
||||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue